Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DebugWidget/DebugWidgetBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SwiftUI
@main
struct StikDebugWidgetBundle: WidgetBundle {
var body: some Widget {
// Both widgets enabled: Favorites uses enable-jit URL scheme (with PiP/script handled in-app),
// Both widgets enabled: Favorites uses enable-jit URL scheme (with continued-processing handled in-app),
// System Apps uses launch-app URL scheme for non-debug launch behavior.
FavoritesWidget()
SystemAppsWidget()
Expand Down
17 changes: 0 additions & 17 deletions StikDebug.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
objects = {

/* Begin PBXBuildFile section */
17C744F02E20BED000834F17 /* Pipify in Frameworks */ = {isa = PBXBuildFile; productRef = 17C744EF2E20BED000834F17 /* Pipify */; };
687843662E8765D1002D0813 /* ZSignApple in Frameworks */ = {isa = PBXBuildFile; productRef = 687843652E8765D1002D0813 /* ZSignApple */; };
68D1FA402E847E4A0028A0EA /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D1FA3F2E847E4A0028A0EA /* StoreKit.framework */; };
68D569BE2E1B415700A5BA36 /* CodeEditorView in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BD2E1B415700A5BA36 /* CodeEditorView */; };
Expand Down Expand Up @@ -156,7 +155,6 @@
buildActionMask = 2147483647;
files = (
DCBA85862E3897BD00E88C06 /* StikImporter in Frameworks */,
17C744F02E20BED000834F17 /* Pipify in Frameworks */,
68D569C02E1B415700A5BA36 /* LanguageSupport in Frameworks */,
68D1FA402E847E4A0028A0EA /* StoreKit.framework in Frameworks */,
68D569BE2E1B415700A5BA36 /* CodeEditorView in Frameworks */,
Expand Down Expand Up @@ -274,7 +272,6 @@
packageProductDependencies = (
68D569BD2E1B415700A5BA36 /* CodeEditorView */,
68D569BF2E1B415700A5BA36 /* LanguageSupport */,
17C744EF2E20BED000834F17 /* Pipify */,
DCBA85852E3897BD00E88C06 /* StikImporter */,
68E714E52E6AA2B00025610F /* ZIPFoundation */,
687843652E8765D1002D0813 /* ZSignApple */,
Expand Down Expand Up @@ -393,7 +390,6 @@
minimizedProjectReferenceProxies = 1;
packageReferences = (
68D569BC2E1B415700A5BA36 /* XCRemoteSwiftPackageReference "CodeEditorView" */,
17C744EE2E20BED000834F17 /* XCRemoteSwiftPackageReference "swiftui-pipify" */,
DCBA85842E3897BD00E88C06 /* XCRemoteSwiftPackageReference "StikImporter" */,
68E714E42E6AA2B00025610F /* XCRemoteSwiftPackageReference "ZIPFoundation" */,
687843642E8765D0002D0813 /* XCRemoteSwiftPackageReference "zsign-swift" */,
Expand Down Expand Up @@ -999,14 +995,6 @@
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
17C744EE2E20BED000834F17 /* XCRemoteSwiftPackageReference "swiftui-pipify" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/hugeBlack/swiftui-pipify";
requirement = {
branch = main;
kind = branch;
};
};
687843642E8765D0002D0813 /* XCRemoteSwiftPackageReference "zsign-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/loyahdev/zsign-swift";
Expand Down Expand Up @@ -1042,11 +1030,6 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
17C744EF2E20BED000834F17 /* Pipify */ = {
isa = XCSwiftPackageProductDependency;
package = 17C744EE2E20BED000834F17 /* XCRemoteSwiftPackageReference "swiftui-pipify" */;
productName = Pipify;
};
687843652E8765D1002D0813 /* ZSignApple */ = {
isa = XCSwiftPackageProductDependency;
package = 687843642E8765D0002D0813 /* XCRemoteSwiftPackageReference "zsign-swift" */;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions StikJIT/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).continuedProcessing</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
Expand Down
27 changes: 2 additions & 25 deletions StikJIT/JSSupport/RunJSView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,36 +78,13 @@ class RunJSViewModel: ObservableObject {
if let exception = self.context?.exception {
self.logs.append(exception.debugDescription)
}

self.logs.append("Script Execution Completed")
self.logs.append("You are safe to close the PIP Window.")
}
}
}

struct RunJSViewPiP: View {
@Binding var model: RunJSViewModel?
@State var logs: [String] = []
let timer = Timer.publish(every: 0.034, on: .main, in: .common).autoconnect()


var body: some View {
VStack(alignment: .leading, spacing: 4) {
ForEach(logs.suffix(6).indices, id: \.self) { index in
Text(logs.suffix(6)[index])
.font(.system(size: 12))
.foregroundStyle(.white)
}
}
.padding()
.onReceive(timer) { _ in
self.logs = model?.logs ?? []
self.logs.append("Script Execution Completed")
self.logs.append("You can safely leave StikDebug once this window is closed.")
}
.frame(width: 300, height: 150)
}
}


struct RunJSView: View {
@ObservedObject var model: RunJSViewModel

Expand Down
1 change: 1 addition & 0 deletions StikJIT/StikJITApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ struct HeartbeatApp: App {
init() {
registerAdvancedOptionsDefault()
newVerCheck()
BackgroundContinuation.setup()
let fixMethod = class_getInstanceMethod(UIDocumentPickerViewController.self, #selector(UIDocumentPickerViewController.fix_init(forOpeningContentTypes:asCopy:)))!
let origMethod = class_getInstanceMethod(UIDocumentPickerViewController.self, #selector(UIDocumentPickerViewController.init(forOpeningContentTypes:asCopy:)))!
method_exchangeImplementations(origMethod, fixMethod)
Expand Down
147 changes: 147 additions & 0 deletions StikJIT/Utilities/BackgroundContinuation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import Foundation
import OSLog

#if canImport(BackgroundTasks)
import BackgroundTasks
#endif

/// Represents an active continued-processing request so the caller can close it explicitly.
struct BackgroundContinuationToken: Hashable {
fileprivate let id = UUID()
}

enum BackgroundContinuation {
private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.stik.StikJIT", category: "Background")

static func setup() {
#if canImport(BackgroundTasks)
if #available(iOS 26.0, *) {
BGContinuationController.shared.setup()
}
#endif
}

@discardableResult
static func begin(title: String, subtitle: String) -> BackgroundContinuationToken? {
#if canImport(BackgroundTasks)
if #available(iOS 26.0, *) {
return BGContinuationController.shared.begin(title: title, subtitle: subtitle)
}
#endif
logger.debug("BGContinuedProcessingTask unavailable on this OS")
return nil
}

static func end(success: Bool, token: BackgroundContinuationToken?) {
guard let token else { return }
#if canImport(BackgroundTasks)
if #available(iOS 26.0, *) {
BGContinuationController.shared.end(success: success, token: token)
return
}
#endif
logger.debug("No continued-processing task to finish")
}

static var isAvailable: Bool {
#if canImport(BackgroundTasks)
if #available(iOS 26.0, *) {
return BGContinuationController.shared.isConfigured
}
#endif
return false
}
}

#if canImport(BackgroundTasks)
@available(iOS 26.0, *)
private final class BGContinuationController {
static let shared = BGContinuationController()

private let scheduler = BGTaskScheduler.shared
private let taskIdentifier: String
private var registered = false
private var pendingToken: BackgroundContinuationToken?
private var activeToken: BackgroundContinuationToken?
private var activeTask: BGContinuedProcessingTask?
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.stik.StikJIT", category: "Background")

private init() {
let baseIdentifier = Bundle.main.bundleIdentifier ?? "com.stik.StikJIT"
taskIdentifier = baseIdentifier + ".continuedProcessing"
}

var isConfigured: Bool { registered }

func setup() {
guard !registered else { return }
registered = scheduler.register(forTaskWithIdentifier: taskIdentifier, using: nil) { [weak self] task in
self?.handle(task: task)
}
if !registered {
logger.error("Failed to register continued-processing handler")
LogManager.shared.addErrorLog("Unable to register continued processing background handler.")
} else {
logger.debug("Registered continued-processing handler")
}
}

func begin(title: String, subtitle: String) -> BackgroundContinuationToken? {
setup()
guard registered else { return nil }

let request = BGContinuedProcessingTaskRequest(identifier: taskIdentifier, title: title, subtitle: subtitle)
request.strategy = .queue

let token = BackgroundContinuationToken()
do {
try scheduler.submit(request)
pendingToken = token
logger.info("Submitted continued-processing task request: \(title, privacy: .public)")
LogManager.shared.addInfoLog("Requested continued processing for \(title)")
return token
} catch {
logger.error("Failed to submit continued-processing task: \(error.localizedDescription, privacy: .public)")
LogManager.shared.addErrorLog("Unable to request continued processing: \(error.localizedDescription)")
return nil
}
}

func end(success: Bool, token: BackgroundContinuationToken) {
if pendingToken == token {
scheduler.cancel(taskRequestWithIdentifier: taskIdentifier)
pendingToken = nil
}

if activeToken == token {
activeTask?.setTaskCompleted(success: success)
activeTask = nil
activeToken = nil
logger.info("Marked continued-processing task as completed: success=\(success)")
LogManager.shared.addInfoLog(success ? "Background continued processing finished." : "Background continued processing ended early.")
}
}

private func handle(task: BGTask) {
guard let continuedTask = task as? BGContinuedProcessingTask else {
task.setTaskCompleted(success: false)
return
}

activeTask = continuedTask
activeToken = pendingToken
pendingToken = nil

continuedTask.expirationHandler = { [weak self] in
guard let self else { return }
self.logger.warning("Continued-processing task expired")
self.activeTask?.setTaskCompleted(success: false)
self.activeTask = nil
if let activeToken {
LogManager.shared.addWarningLog("Background continued processing expired before completion.")
}
self.activeToken = nil
}
}
}
#endif
Loading