diff --git a/DebugWidget/DebugWidgetBundle.swift b/DebugWidget/DebugWidgetBundle.swift
index 77bf06e..10b39be 100644
--- a/DebugWidget/DebugWidgetBundle.swift
+++ b/DebugWidget/DebugWidgetBundle.swift
@@ -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()
diff --git a/StikDebug.xcodeproj/project.pbxproj b/StikDebug.xcodeproj/project.pbxproj
index 507ead8..f3caf5c 100644
--- a/StikDebug.xcodeproj/project.pbxproj
+++ b/StikDebug.xcodeproj/project.pbxproj
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
- 17C744F02E20BED000834F17 /* Pipify in Frameworks */ = {isa = PBXBuildFile; productRef = 17C744EF2E20BED000834F17 /* Pipify */; };
68D1FA402E847E4A0028A0EA /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68D1FA3F2E847E4A0028A0EA /* StoreKit.framework */; };
68D569BE2E1B415700A5BA36 /* CodeEditorView in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BD2E1B415700A5BA36 /* CodeEditorView */; };
68D569C02E1B415700A5BA36 /* LanguageSupport in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BF2E1B415700A5BA36 /* LanguageSupport */; };
@@ -155,7 +154,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 */,
@@ -272,7 +270,6 @@
packageProductDependencies = (
68D569BD2E1B415700A5BA36 /* CodeEditorView */,
68D569BF2E1B415700A5BA36 /* LanguageSupport */,
- 17C744EF2E20BED000834F17 /* Pipify */,
DCBA85852E3897BD00E88C06 /* StikImporter */,
68E714E52E6AA2B00025610F /* ZIPFoundation */,
);
@@ -390,7 +387,6 @@
minimizedProjectReferenceProxies = 1;
packageReferences = (
68D569BC2E1B415700A5BA36 /* XCRemoteSwiftPackageReference "CodeEditorView" */,
- 17C744EE2E20BED000834F17 /* XCRemoteSwiftPackageReference "swiftui-pipify" */,
DCBA85842E3897BD00E88C06 /* XCRemoteSwiftPackageReference "StikImporter" */,
68E714E42E6AA2B00025610F /* XCRemoteSwiftPackageReference "ZIPFoundation" */,
);
@@ -997,14 +993,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;
- };
- };
68D569BC2E1B415700A5BA36 /* XCRemoteSwiftPackageReference "CodeEditorView" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/mchakravarty/CodeEditorView";
@@ -1032,11 +1020,6 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
- 17C744EF2E20BED000834F17 /* Pipify */ = {
- isa = XCSwiftPackageProductDependency;
- package = 17C744EE2E20BED000834F17 /* XCRemoteSwiftPackageReference "swiftui-pipify" */;
- productName = Pipify;
- };
68D569BD2E1B415700A5BA36 /* CodeEditorView */ = {
isa = XCSwiftPackageProductDependency;
package = 68D569BC2E1B415700A5BA36 /* XCRemoteSwiftPackageReference "CodeEditorView" */;
diff --git a/StikDebug.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/StikDebug.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 7da08b0..ce50db7 100644
--- a/StikDebug.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/StikDebug.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -1,5 +1,5 @@
{
- "originHash" : "ac6355f0d394d08e98cd8bc09669552151ca717b83a414b5708b301c9ba767e4",
+ "originHash" : "0c22ab9d76f71ac04ee3ae6420154b1551511330120649c0ec71bbeb02f33fd5",
"pins" : [
{
"identity" : "codeeditorview",
@@ -28,15 +28,6 @@
"version" : "1.0.2"
}
},
- {
- "identity" : "swiftui-pipify",
- "kind" : "remoteSourceControl",
- "location" : "https://github.com/hugeBlack/swiftui-pipify",
- "state" : {
- "branch" : "main",
- "revision" : "a1ec2fd1781c8289bff1a8b3f664dcf21c910efb"
- }
- },
{
"identity" : "zipfoundation",
"kind" : "remoteSourceControl",
diff --git a/StikJIT/Info.plist b/StikJIT/Info.plist
index ece1afd..d3c8eff 100644
--- a/StikJIT/Info.plist
+++ b/StikJIT/Info.plist
@@ -15,9 +15,14 @@
+ BGTaskSchedulerPermittedIdentifiers
+
+ $(PRODUCT_BUNDLE_IDENTIFIER).continuedProcessingTask.script
+
UIBackgroundModes
audio
+ processing
UIFileSharingEnabled
diff --git a/StikJIT/JSSupport/RunJSView.swift b/StikJIT/JSSupport/RunJSView.swift
index 5c97a92..d09d713 100644
--- a/StikJIT/JSSupport/RunJSView.swift
+++ b/StikJIT/JSSupport/RunJSView.swift
@@ -16,6 +16,8 @@ class RunJSViewModel: ObservableObject {
var pid: Int
var debugProxy: OpaquePointer?
var semaphore: dispatch_semaphore_t?
+ private var progressTimer: DispatchSourceTimer?
+ private var reportedProgress: Double = 0
init(pid: Int, debugProxy: OpaquePointer?, semaphore: dispatch_semaphore_t?) {
self.pid = pid
@@ -30,6 +32,7 @@ class RunJSViewModel: ObservableObject {
func runScript(data: Data, name: String? = nil) throws {
let scriptContent = String(data: data, encoding: .utf8)
scriptName = name ?? "Script"
+ startContinuedProcessing(withTitle: scriptName)
let getPidFunction: @convention(block) () -> Int = {
return self.pid
@@ -73,37 +76,48 @@ class RunJSViewModel: ObservableObject {
if let semaphore {
semaphore.signal()
}
-
+
DispatchQueue.main.async {
if let exception = self.context?.exception {
self.logs.append(exception.debugDescription)
}
-
+ let success = self.context?.exception == nil && !self.executionInterrupted
+ self.stopContinuedProcessing(success: success)
self.logs.append("Script Execution Completed")
- self.logs.append("You are safe to close the PIP Window.")
+ self.logs.append("Background processing finished. You can dismiss this view.")
}
}
-}
-
-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)
+ private func startContinuedProcessing(withTitle title: String) {
+ guard ContinuedProcessingManager.shared.isSupported,
+ UserDefaults.standard.bool(forKey: UserDefaults.Keys.enableContinuedProcessing) else { return }
+ stopProgressTimer()
+ reportedProgress = 0.05
+ ContinuedProcessingManager.shared.begin(title: title, subtitle: "Script execution in progress")
+ ContinuedProcessingManager.shared.updateProgress(reportedProgress)
+ let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .background))
+ timer.schedule(deadline: .now() + 5, repeating: 5)
+ timer.setEventHandler { [weak self] in
+ guard let self else { return }
+ self.reportedProgress = min(0.9, self.reportedProgress + 0.1)
+ ContinuedProcessingManager.shared.updateProgress(self.reportedProgress)
+ if self.reportedProgress >= 0.9 {
+ self.stopProgressTimer()
}
}
- .padding()
- .onReceive(timer) { _ in
- self.logs = model?.logs ?? []
- }
- .frame(width: 300, height: 150)
+ timer.resume()
+ progressTimer = timer
+ }
+
+ private func stopContinuedProcessing(success: Bool) {
+ stopProgressTimer()
+ ContinuedProcessingManager.shared.updateProgress(1.0)
+ ContinuedProcessingManager.shared.finish(success: success)
+ }
+
+ private func stopProgressTimer() {
+ progressTimer?.cancel()
+ progressTimer = nil
}
}
diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift
index 9861f2c..d0e62f1 100644
--- a/StikJIT/StikJITApp.swift
+++ b/StikJIT/StikJITApp.swift
@@ -15,9 +15,17 @@ private func registerAdvancedOptionsDefault() {
let os = ProcessInfo.processInfo.operatingSystemVersion
// Enable advanced options by default on iOS 19/26 and above
let enabled = os.majorVersion >= 19
- UserDefaults.standard.register(defaults: ["enableAdvancedOptions": enabled])
- UserDefaults.standard.register(defaults: ["enablePiP": enabled])
- UserDefaults.standard.register(defaults: [UserDefaults.Keys.txmOverride: false])
+ let defaults = UserDefaults.standard
+ defaults.register(defaults: ["enableAdvancedOptions": enabled])
+ defaults.register(defaults: [UserDefaults.Keys.txmOverride: false])
+ if defaults.object(forKey: UserDefaults.Keys.enableContinuedProcessing) == nil {
+ let legacyPiP = defaults.object(forKey: "enablePiP") as? Bool
+ if let legacyPiP {
+ defaults.set(legacyPiP, forKey: UserDefaults.Keys.enableContinuedProcessing)
+ } else {
+ defaults.register(defaults: [UserDefaults.Keys.enableContinuedProcessing: enabled])
+ }
+ }
}
// MARK: - Welcome Sheet
@@ -519,6 +527,7 @@ struct HeartbeatApp: App {
init() {
registerAdvancedOptionsDefault()
+ ContinuedProcessingManager.shared.configureIfNeeded()
newVerCheck()
let fixMethod = class_getInstanceMethod(UIDocumentPickerViewController.self, #selector(UIDocumentPickerViewController.fix_init(forOpeningContentTypes:asCopy:)))!
let origMethod = class_getInstanceMethod(UIDocumentPickerViewController.self, #selector(UIDocumentPickerViewController.init(forOpeningContentTypes:asCopy:)))!
diff --git a/StikJIT/Utilities/ContinuedProcessingManager.swift b/StikJIT/Utilities/ContinuedProcessingManager.swift
new file mode 100644
index 0000000..b358020
--- /dev/null
+++ b/StikJIT/Utilities/ContinuedProcessingManager.swift
@@ -0,0 +1,157 @@
+//
+// ContinuedProcessingManager.swift
+// StikJIT
+//
+// Created by Codex on 11/20/24.
+//
+
+import Foundation
+import BackgroundTasks
+
+final class ContinuedProcessingManager {
+ static let shared = ContinuedProcessingManager()
+ private let handler: ContinuedProcessingHandling
+
+ private init() {
+ if #available(iOS 26.0, *) {
+ handler = ModernContinuedProcessingHandler()
+ } else {
+ handler = NoopContinuedProcessingHandler()
+ }
+ }
+
+ var isSupported: Bool { handler.isSupported }
+
+ func configureIfNeeded() {
+ handler.configureIfNeeded()
+ }
+
+ func begin(title: String, subtitle: String) {
+ handler.begin(title: title, subtitle: subtitle)
+ }
+
+ func updateProgress(_ fraction: Double) {
+ handler.updateProgress(fraction)
+ }
+
+ func finish(success: Bool) {
+ handler.finish(success: success)
+ }
+}
+
+private protocol ContinuedProcessingHandling: AnyObject {
+ var isSupported: Bool { get }
+ func configureIfNeeded()
+ func begin(title: String, subtitle: String)
+ func updateProgress(_ fraction: Double)
+ func finish(success: Bool)
+}
+
+private final class NoopContinuedProcessingHandler: ContinuedProcessingHandling {
+ var isSupported: Bool { false }
+ func configureIfNeeded() {}
+ func begin(title: String, subtitle: String) {}
+ func updateProgress(_ fraction: Double) {}
+ func finish(success: Bool) {}
+}
+
+@available(iOS 26.0, *)
+private final class ModernContinuedProcessingHandler: ContinuedProcessingHandling {
+ private let scheduler = BGTaskScheduler.shared
+ private let taskIdentifier: String
+ private var didRegister = false
+ private var activeTask: BGContinuedProcessingTask?
+ private let queue = DispatchQueue(label: "com.stikdebug.continuedProcessing",
+ qos: .utility)
+ private var pendingMetadata: (title: String, subtitle: String)?
+
+ init() {
+ let bundleID = Bundle.main.bundleIdentifier ?? "com.stik.sj"
+ taskIdentifier = "\(bundleID).continuedProcessingTask.script"
+ }
+
+ var isSupported: Bool { true }
+
+ func configureIfNeeded() {
+ guard !didRegister else { return }
+ scheduler.register(forTaskWithIdentifier: taskIdentifier, using: nil) { [weak self] task in
+ guard let continuedTask = task as? BGContinuedProcessingTask else {
+ task.setTaskCompleted(success: false)
+ return
+ }
+ self?.handle(task: continuedTask)
+ }
+ didRegister = true
+ }
+
+ func begin(title: String, subtitle: String) {
+ guard UserDefaults.standard.bool(forKey: UserDefaults.Keys.enableContinuedProcessing) else { return }
+ configureIfNeeded()
+ var reserved = false
+ queue.sync {
+ if activeTask == nil && pendingMetadata == nil {
+ pendingMetadata = (title: title, subtitle: subtitle)
+ reserved = true
+ }
+ }
+ guard reserved else { return }
+ let request = BGContinuedProcessingTaskRequest(identifier: taskIdentifier,
+ title: title,
+ subtitle: subtitle)
+ request.strategy = .queue
+ do {
+ try scheduler.submit(request)
+ LogManager.shared.addInfoLog("Requested continued processing: \(title)")
+ } catch {
+ LogManager.shared.addWarningLog("Unable to request continued processing: \(error.localizedDescription)")
+ queue.async { [weak self] in
+ self?.pendingMetadata = nil
+ }
+ }
+ }
+
+ func updateProgress(_ fraction: Double) {
+ queue.async { [weak self] in
+ guard let task = self?.activeTask else { return }
+ let clamped = max(0.0, min(1.0, fraction))
+ task.progress.totalUnitCount = max(task.progress.totalUnitCount, 100)
+ task.progress.completedUnitCount = Int64(Double(task.progress.totalUnitCount) * clamped)
+ }
+ }
+
+ func finish(success: Bool) {
+ queue.async { [weak self] in
+ guard let self else { return }
+ if let task = self.activeTask {
+ task.progress.completedUnitCount = task.progress.totalUnitCount
+ task.setTaskCompleted(success: success)
+ self.activeTask = nil
+ } else if pendingMetadata != nil {
+ scheduler.cancel(taskRequestWithIdentifier: taskIdentifier)
+ }
+ pendingMetadata = nil
+ }
+ }
+
+ private func handle(task: BGContinuedProcessingTask) {
+ queue.async { [weak self] in
+ guard let self else { return }
+ activeTask = task
+ if let metadata = pendingMetadata {
+ task.updateTitle(metadata.title, subtitle: metadata.subtitle)
+ }
+ if task.progress.totalUnitCount == 0 {
+ task.progress.totalUnitCount = 100
+ }
+ task.progress.completedUnitCount = 1
+ task.expirationHandler = { [weak self] in
+ self?.handleExpiration()
+ }
+ }
+ }
+
+ private func handleExpiration() {
+ LogManager.shared.addWarningLog("Continued processing expired early")
+ finish(success: false)
+ }
+}
diff --git a/StikJIT/Utilities/Extensions.swift b/StikJIT/Utilities/Extensions.swift
index 31e66a5..55e50ff 100644
--- a/StikJIT/Utilities/Extensions.swift
+++ b/StikJIT/Utilities/Extensions.swift
@@ -23,5 +23,7 @@ extension UserDefaults {
enum Keys {
/// Forces the app to treat the current device as TXM-capable so scripts always run.
static let txmOverride = "overrideTXMForScripts"
+ /// Controls whether BGContinuedProcessingTask should be used to keep scripts alive in the background.
+ static let enableContinuedProcessing = "enableContinuedProcessing"
}
}
diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift
index 90ed630..153218b 100644
--- a/StikJIT/Views/HomeView.swift
+++ b/StikJIT/Views/HomeView.swift
@@ -7,7 +7,6 @@
import SwiftUI
import UniformTypeIdentifiers
-import Pipify
import UIKit
import WidgetKit
import Combine
@@ -51,9 +50,7 @@ struct HomeView: View {
@AppStorage("enableAdvancedOptions") private var enableAdvancedOptions = false
@AppStorage("useDefaultScript") private var useDefaultScript = false
- @AppStorage("enablePiP") private var enablePiP = true
@State var scriptViewShow = false
- @State var pipRequired = false
@AppStorage("DefaultScriptName") var selectedScript = "attachDetach.js"
@State var jsModel: RunJSViewModel?
@@ -190,6 +187,7 @@ struct HomeView: View {
}
.onChange(of: favoriteApps) { _, _ in
loadAppListIfNeeded()
+ syncFavoriteAppNamesWithCache()
}
.onChange(of: recentApps) { _, _ in
loadAppListIfNeeded()
@@ -250,42 +248,9 @@ struct HomeView: View {
var autoScriptData: Data? = nil
var autoScriptName: String? = nil
- let appName: String? = (try? JITEnableContext.shared.getAppList()[selectedBundle])
-
- if #available(iOS 26, *) {
- if ProcessInfo.processInfo.hasTXM, let appName {
- if appName == "maciOS" {
- if let url = Bundle.main.url(forResource: "script1", withExtension: "js"),
- let data = try? Data(contentsOf: url) {
- autoScriptData = data
- autoScriptName = "script1.js"
- }
- } else if appName == "Amethyst" {
- if let url = Bundle.main.url(forResource: "script2", withExtension: "js"),
- let data = try? Data(contentsOf: url) {
- autoScriptData = data
- autoScriptName = "script2.js"
- }
- } else if appName == "Geode" {
- if let url = Bundle.main.url(forResource: "Geode", withExtension: "js"),
- let data = try? Data(contentsOf: url) {
- autoScriptData = data
- autoScriptName = "Geode.js"
- }
- } else if appName == "MeloNX" {
- if let url = Bundle.main.url(forResource: "melo", withExtension: "js"),
- let data = try? Data(contentsOf: url) {
- autoScriptData = data
- autoScriptName = "melo.js"
- }
- } else if appName == "UTM" || appName == "DolphiniOS" {
- if let url = Bundle.main.url(forResource: "utmjit", withExtension: "js"),
- let data = try? Data(contentsOf: url) {
- autoScriptData = data
- autoScriptName = "utmjit.js"
- }
- }
- }
+ if let scriptInfo = autoScript(for: selectedBundle) {
+ autoScriptData = scriptInfo.data
+ autoScriptName = scriptInfo.name
}
startJITInBackground(bundleID: selectedBundle,
@@ -295,12 +260,6 @@ struct HomeView: View {
triggeredByURLScheme: false)
}
}
- .pipify(isPresented: Binding(
- get: { pipRequired && enablePiP },
- set: { newValue in pipRequired = newValue }
- )) {
- RunJSViewPiP(model: $jsModel)
- }
.sheet(isPresented: $scriptViewShow) {
NavigationView {
if let jsModel {
@@ -346,6 +305,11 @@ struct HomeView: View {
if let scriptName = components?.queryItems?.first(where: { $0.name == "script-name" })?.value {
config.scriptName = scriptName
}
+ if config.scriptData == nil, let bundleID = config.bundleID,
+ let scriptInfo = autoScript(for: bundleID) {
+ config.scriptData = scriptInfo.data
+ config.scriptName = scriptInfo.name
+ }
if viewDidAppeared {
startJITInBackground(bundleID: config.bundleID, pid: config.pid, scriptData: config.scriptData, scriptName: config.scriptName, triggeredByURLScheme: true)
} else {
@@ -1033,6 +997,7 @@ struct HomeView: View {
if let restoredApps, cachedAppNames.isEmpty {
cachedAppNames = restoredApps
+ syncFavoriteAppNamesWithCache()
}
refreshBackground()
@@ -1062,6 +1027,7 @@ struct HomeView: View {
let encoded = try? JSONEncoder().encode(result)
DispatchQueue.main.async {
cachedAppNames = result
+ syncFavoriteAppNamesWithCache()
cachedAppNamesData = encoded
}
}
@@ -1253,6 +1219,65 @@ struct HomeView: View {
}
private func refreshBackground() { }
+ private func autoScript(for bundleID: String) -> (data: Data, name: String)? {
+ guard ProcessInfo.processInfo.hasTXM else { return nil }
+ guard #available(iOS 26, *) else { return nil }
+ let appName = (try? JITEnableContext.shared.getAppList()[bundleID]) ?? storedFavoriteName(for: bundleID)
+ guard let appName,
+ let resource = autoScriptResource(for: appName),
+ let url = Bundle.main.url(forResource: resource.resource, withExtension: "js"),
+ let data = try? Data(contentsOf: url) else {
+ return nil
+ }
+ return (data, resource.fileName)
+ }
+
+ private func storedFavoriteName(for bundleID: String) -> String? {
+ let defaults = UserDefaults(suiteName: "group.com.stik.sj")
+ let names = defaults?.dictionary(forKey: "favoriteAppNames") as? [String: String]
+ return names?[bundleID]
+ }
+
+ private func syncFavoriteAppNamesWithCache() {
+ guard let sharedDefaults = UserDefaults(suiteName: "group.com.stik.sj") else { return }
+ let favorites = sharedDefaults.stringArray(forKey: "favoriteApps") ?? []
+ guard !favorites.isEmpty else { return }
+
+ var storedNames = (sharedDefaults.dictionary(forKey: "favoriteAppNames") as? [String: String]) ?? [:]
+ var changed = false
+
+ for bundle in favorites {
+ guard let rawName = cachedAppNames[bundle], !rawName.isEmpty else { continue }
+ let display = shortDisplayName(from: rawName)
+ if storedNames[bundle] != display {
+ storedNames[bundle] = display
+ changed = true
+ }
+ }
+
+ if changed {
+ sharedDefaults.set(storedNames, forKey: "favoriteAppNames")
+ WidgetCenter.shared.reloadTimelines(ofKind: "FavoritesWidget")
+ }
+ }
+
+ private func autoScriptResource(for appName: String) -> (resource: String, fileName: String)? {
+ switch appName {
+ case "maciOS":
+ return ("script1", "script1.js")
+ case "Amethyst":
+ return ("script2", "script2.js")
+ case "Geode":
+ return ("Geode", "Geode.js")
+ case "MeloNX":
+ return ("melo", "melo.js")
+ case "UTM", "DolphiniOS":
+ return ("utmjit", "utmjit.js")
+ default:
+ return nil
+ }
+ }
+
private func getJsCallback(_ script: Data, name: String? = nil) -> DebugAppCallback {
return { pid, debugProxyHandle, semaphore in
jsModel = RunJSViewModel(pid: Int(pid), debugProxy: debugProxyHandle, semaphore: semaphore)
@@ -1291,9 +1316,6 @@ struct HomeView: View {
if ProcessInfo.processInfo.hasTXM, let sd = scriptData {
callback = getJsCallback(sd, name: scriptName ?? bundleID ?? "Script")
if triggeredByURLScheme { usleep(500000) }
- pipRequired = true
- } else {
- pipRequired = false
}
let logger: LogFunc = { message in if let message { LogManager.shared.addInfoLog(message) } }
@@ -1316,7 +1338,6 @@ struct HomeView: View {
}
}
isProcessing = false
- pipRequired = false
}
}
diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift
index 4e58f08..05b44e0 100644
--- a/StikJIT/Views/SettingsView.swift
+++ b/StikJIT/Views/SettingsView.swift
@@ -14,7 +14,7 @@ struct SettingsView: View {
@AppStorage("enableAdvancedOptions") private var enableAdvancedOptions = false
@AppStorage("enableAdvancedBetaOptions") private var enableAdvancedBetaOptions = false
@AppStorage("enableTesting") private var enableTesting = false
- @AppStorage("enablePiP") private var enablePiP = false
+ @AppStorage(UserDefaults.Keys.enableContinuedProcessing) private var enableContinuedProcessing = false
@AppStorage(UserDefaults.Keys.txmOverride) private var overrideTXMDetection = false
@AppStorage("customAccentColor") private var customAccentColorHex: String = ""
@AppStorage("appTheme") private var appThemeRaw: String = AppTheme.system.rawValue
@@ -332,8 +332,7 @@ struct SettingsView: View {
Toggle("Run Default Script After Connecting", isOn: $useDefaultScript)
.tint(accentColor)
- Toggle("Picture in Picture", isOn: $enablePiP)
- .tint(accentColor)
+ continuedProcessingToggle
Toggle(isOn: $overrideTXMDetection) {
VStack(alignment: .leading, spacing: 2) {
Text("Always Run Scripts")
@@ -349,7 +348,7 @@ struct SettingsView: View {
.onChange(of: enableAdvancedOptions) { _, newValue in
if !newValue {
useDefaultScript = false
- enablePiP = false
+ enableContinuedProcessing = false
enableAdvancedBetaOptions = false
enableTesting = false
}
@@ -397,6 +396,24 @@ struct SettingsView: View {
}
}
}
+
+ private var continuedProcessingToggle: some View {
+ Group {
+ if ContinuedProcessingManager.shared.isSupported {
+ Toggle("Allow Continued Processing", isOn: $enableContinuedProcessing)
+ .tint(accentColor)
+ } else {
+ VStack(alignment: .leading, spacing: 4) {
+ Toggle("Allow Continued Processing", isOn: .constant(false))
+ .tint(accentColor)
+ .disabled(true)
+ Text("Requires iOS 26 or later.")
+ .font(.caption)
+ .foregroundColor(.secondary)
+ }
+ }
+ }
+ }
private var helpCard: some View {
glassCard {