diff --git a/StikDebug.xcodeproj/project.pbxproj b/StikDebug.xcodeproj/project.pbxproj index cfbdc12..1acfce6 100644 --- a/StikDebug.xcodeproj/project.pbxproj +++ b/StikDebug.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 68D569BE2E1B415700A5BA36 /* CodeEditorView in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BD2E1B415700A5BA36 /* CodeEditorView */; }; 68D569C02E1B415700A5BA36 /* LanguageSupport in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BF2E1B415700A5BA36 /* LanguageSupport */; }; - F00DCAFE000000000000E00B /* StikDebugWidgets.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = F00DCAFE000000000000E002 /* StikDebugWidgets.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -27,13 +26,6 @@ remoteGlobalIDString = DC6F1D362D94EADD0071B2B6; remoteInfo = StikDebug; }; - F00DCAFE000000000000E00D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = DC6F1D2F2D94EADD0071B2B6 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F00DCAFE000000000000E001; - remoteInfo = StikDebugWidgets; - }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -43,7 +35,6 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( - F00DCAFE000000000000E00B /* StikDebugWidgets.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -56,7 +47,6 @@ DC6F1D372D94EADD0071B2B6 /* StikDebug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StikDebug.app; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D482D94EADF0071B2B6 /* StikDebugTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikDebugTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D522D94EADF0071B2B6 /* StikDebugUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikDebugUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F00DCAFE000000000000E002 /* StikDebugWidgets.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = StikDebugWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -67,13 +57,6 @@ ); target = DC6F1D362D94EADD0071B2B6 /* StikDebug */; }; - F00DCAFE000000000000E00E /* Exceptions for "StikDebugWidgets" folder in "StikDebugWidgets" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = F00DCAFE000000000000E001 /* StikDebugWidgets */; - }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -85,19 +68,6 @@ path = StikDebug; sourceTree = ""; }; - F00DCAFE000000000000E006 /* StikDebugWidgets */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - F00DCAFE000000000000E00E /* Exceptions for "StikDebugWidgets" folder in "StikDebugWidgets" target */, - ); - path = StikDebugWidgets; - sourceTree = ""; - }; - F00DCAFE000000000000E007 /* StikDebugShared */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = StikDebugShared; - sourceTree = ""; - }; DC6F1D4B2D94EADF0071B2B6 /* StikDebugTests */ = { isa = PBXFileSystemSynchronizedRootGroup; path = StikDebugTests; @@ -134,13 +104,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F00DCAFE000000000000E004 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -148,8 +111,6 @@ isa = PBXGroup; children = ( DC6F1D392D94EADD0071B2B6 /* StikDebug */, - F00DCAFE000000000000E007 /* StikDebugShared */, - F00DCAFE000000000000E006 /* StikDebugWidgets */, DC6F1D4B2D94EADF0071B2B6 /* StikDebugTests */, DC6F1D552D94EADF0071B2B6 /* StikDebugUITests */, DC6F1D752D94EB620071B2B6 /* Frameworks */, @@ -163,7 +124,6 @@ DC6F1D372D94EADD0071B2B6 /* StikDebug.app */, DC6F1D482D94EADF0071B2B6 /* StikDebugTests.xctest */, DC6F1D522D94EADF0071B2B6 /* StikDebugUITests.xctest */, - F00DCAFE000000000000E002 /* StikDebugWidgets.appex */, ); name = Products; sourceTree = ""; @@ -192,11 +152,9 @@ buildRules = ( ); dependencies = ( - F00DCAFE000000000000E00C /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( DC6F1D392D94EADD0071B2B6 /* StikDebug */, - F00DCAFE000000000000E007 /* StikDebugShared */, ); name = StikDebug; packageProductDependencies = ( @@ -207,29 +165,6 @@ productReference = DC6F1D372D94EADD0071B2B6 /* StikDebug.app */; productType = "com.apple.product-type.application"; }; - F00DCAFE000000000000E001 /* StikDebugWidgets */ = { - isa = PBXNativeTarget; - buildConfigurationList = F00DCAFE000000000000E008 /* Build configuration list for PBXNativeTarget "StikDebugWidgets" */; - buildPhases = ( - F00DCAFE000000000000E003 /* Sources */, - F00DCAFE000000000000E004 /* Frameworks */, - F00DCAFE000000000000E005 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - F00DCAFE000000000000E006 /* StikDebugWidgets */, - F00DCAFE000000000000E007 /* StikDebugShared */, - ); - name = StikDebugWidgets; - packageProductDependencies = ( - ); - productName = StikDebugWidgets; - productReference = F00DCAFE000000000000E002 /* StikDebugWidgets.appex */; - productType = "com.apple.product-type.app-extension"; - }; DC6F1D472D94EADF0071B2B6 /* StikDebugTests */ = { isa = PBXNativeTarget; buildConfigurationList = DC6F1D5F2D94EADF0071B2B6 /* Build configuration list for PBXNativeTarget "StikDebugTests" */; @@ -298,9 +233,6 @@ CreatedOnToolsVersion = 16.2; TestTargetID = DC6F1D362D94EADD0071B2B6; }; - F00DCAFE000000000000E001 = { - CreatedOnToolsVersion = 16.2; - }; }; }; buildConfigurationList = DC6F1D322D94EADD0071B2B6 /* Build configuration list for PBXProject "StikDebug" */; @@ -323,7 +255,6 @@ projectRoot = ""; targets = ( DC6F1D362D94EADD0071B2B6 /* StikDebug */, - F00DCAFE000000000000E001 /* StikDebugWidgets */, DC6F1D472D94EADF0071B2B6 /* StikDebugTests */, DC6F1D512D94EADF0071B2B6 /* StikDebugUITests */, ); @@ -352,13 +283,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F00DCAFE000000000000E005 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -383,13 +307,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F00DCAFE000000000000E003 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -403,11 +320,6 @@ target = DC6F1D362D94EADD0071B2B6 /* StikDebug */; targetProxy = DC6F1D532D94EADF0071B2B6 /* PBXContainerItemProxy */; }; - F00DCAFE000000000000E00C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F00DCAFE000000000000E001 /* StikDebugWidgets */; - targetProxy = F00DCAFE000000000000E00D /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -756,64 +668,6 @@ }; name = Release; }; - F00DCAFE000000000000E009 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = StikDebugWidgets/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = StikDebug; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 17.4; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 3.1.6; - PRODUCT_BUNDLE_IDENTIFIER = com.stik.stikdebug.widgets; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - F00DCAFE000000000000E00A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = StikDebugWidgets/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = StikDebug; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 17.4; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 3.1.6; - PRODUCT_BUNDLE_IDENTIFIER = com.stik.stikdebug.widgets; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -853,15 +707,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F00DCAFE000000000000E008 /* Build configuration list for PBXNativeTarget "StikDebugWidgets" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F00DCAFE000000000000E009 /* Debug */, - F00DCAFE000000000000E00A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/StikDebug/App/AppBootstrapper.swift b/StikDebug/App/AppBootstrapper.swift index 6518d46..4ff12d5 100644 --- a/StikDebug/App/AppBootstrapper.swift +++ b/StikDebug/App/AppBootstrapper.swift @@ -13,9 +13,6 @@ enum AppBootstrapper { startConfiguredKeepAliveServices() applyDocumentPickerCopyWorkaround() NetworkPathMonitor.shared.start() - // No hold is active at launch, so clear any Live Activity a previous - // (possibly force-quit) run left showing in the Dynamic Island. - KeepAliveLiveActivity.endStaleActivities() } private static func registerDefaultSettings() { diff --git a/StikDebug/Info.plist b/StikDebug/Info.plist index 9862fb8..60fb164 100644 --- a/StikDebug/Info.plist +++ b/StikDebug/Info.plist @@ -21,10 +21,6 @@ _stikdebug._tcp _stikdebug._udp - NSSupportsLiveActivities - - NSSupportsLiveActivitiesFrequentUpdates - UIBackgroundModes audio diff --git a/StikDebug/Services/BackgroundAliveManager.swift b/StikDebug/Services/BackgroundAliveManager.swift index 9b2c391..5236301 100644 --- a/StikDebug/Services/BackgroundAliveManager.swift +++ b/StikDebug/Services/BackgroundAliveManager.swift @@ -72,12 +72,7 @@ final class BackgroundAliveManager: ObservableObject { lock.unlock() let name = displayName ?? bundleID - DispatchQueue.main.async { - self.activeAppName = name - // Surface the hold in the Dynamic Island / Lock Screen so it stays - // visible while the user is in the held app. - KeepAliveLiveActivity.start(appName: name, bundleID: bundleID) - } + DispatchQueue.main.async { self.activeAppName = name } LogManager.shared.addInfoLog("Starting background keep-alive for \(name)") DispatchQueue.global(qos: .userInitiated).async { @@ -106,7 +101,6 @@ final class BackgroundAliveManager: ObservableObject { DispatchQueue.main.async { if stillCurrent { self.activeAppName = nil - KeepAliveLiveActivity.end() } if !succeeded { showAlert( diff --git a/StikDebug/Services/KeepAliveLiveActivity.swift b/StikDebug/Services/KeepAliveLiveActivity.swift deleted file mode 100644 index 16b54f7..0000000 --- a/StikDebug/Services/KeepAliveLiveActivity.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// KeepAliveLiveActivity.swift -// StikDebug -// -// App-side controller for the background keep-alive Live Activity. Starts a -// Live Activity when a hold begins so it shows in the Dynamic Island (on -// compatible iPhones) and on the Lock Screen, and ends it when the hold stops. -// - -import Foundation - -#if canImport(ActivityKit) -import ActivityKit -#endif - -enum KeepAliveLiveActivity { - private static let lock = NSLock() - /// Type-erased storage for the current `Activity`, so this enum has no - /// availability requirement of its own and callers don't need to guard. - private static var storage: Any? - - /// Start (or replace) the keep-alive Live Activity for the given app. - static func start(appName: String, bundleID: String) { - #if canImport(ActivityKit) - guard #available(iOS 16.1, *) else { return } - startActivity(appName: appName, bundleID: bundleID) - #endif - } - - /// Update the app name shown by the current Live Activity, if any. - static func update(appName: String) { - #if canImport(ActivityKit) - guard #available(iOS 16.1, *) else { return } - updateActivity(appName: appName) - #endif - } - - /// End the current keep-alive Live Activity, if any. - static func end() { - #if canImport(ActivityKit) - guard #available(iOS 16.1, *) else { return } - endActivity() - #endif - } - - /// End any keep-alive activities left running by a previous launch (for - /// example if the app was force-quit or crashed mid-hold). Call this once at - /// startup, when no hold is active. - static func endStaleActivities() { - #if canImport(ActivityKit) - guard #available(iOS 16.1, *) else { return } - for activity in Activity.activities { - Task { await activity.end(nil, dismissalPolicy: .immediate) } - } - lock.lock() - storage = nil - lock.unlock() - #endif - } - - #if canImport(ActivityKit) - @available(iOS 16.1, *) - private static func startActivity(appName: String, bundleID: String) { - guard ActivityAuthorizationInfo().areActivitiesEnabled else { - LogManager.shared.addInfoLog("Keep-alive Live Activity not started: Live Activities are disabled in Settings") - return - } - - // Only one hold runs at a time, so make sure any stale activity is gone. - endActivity() - - let attributes = KeepAliveActivityAttributes(appName: appName, bundleID: bundleID) - let state = KeepAliveActivityAttributes.ContentState(appName: appName) - - do { - let activity = try Activity.request( - attributes: attributes, - content: ActivityContent(state: state, staleDate: nil) - ) - lock.lock() - storage = activity - lock.unlock() - } catch { - LogManager.shared.addErrorLog("Keep-alive Live Activity failed to start: \(error.localizedDescription)") - } - } - - @available(iOS 16.1, *) - private static func updateActivity(appName: String) { - lock.lock() - let activity = storage as? Activity - lock.unlock() - guard let activity else { return } - - let state = KeepAliveActivityAttributes.ContentState(appName: appName) - Task { - await activity.update(ActivityContent(state: state, staleDate: nil)) - } - } - - @available(iOS 16.1, *) - private static func endActivity() { - lock.lock() - let activity = storage as? Activity - storage = nil - lock.unlock() - guard let activity else { return } - - Task { - await activity.end(nil, dismissalPolicy: .immediate) - } - } - #endif -} diff --git a/StikDebugShared/KeepAliveActivityAttributes.swift b/StikDebugShared/KeepAliveActivityAttributes.swift deleted file mode 100644 index f047efd..0000000 --- a/StikDebugShared/KeepAliveActivityAttributes.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// KeepAliveActivityAttributes.swift -// StikDebug -// -// Shared between the app (which starts/updates/ends the activity) and the -// widget extension (which renders it in the Dynamic Island / on the Lock -// Screen). Keep this file in the StikDebugShared group so both targets compile -// the same type. -// - -import Foundation - -#if canImport(ActivityKit) -import ActivityKit - -@available(iOS 16.1, *) -struct KeepAliveActivityAttributes: ActivityAttributes { - /// The parts of the activity that change while it is live. - struct ContentState: Codable, Hashable { - /// The display name of the app currently being held alive. - var appName: String - } - - /// The app being held alive when the activity started. - var appName: String - /// The bundle identifier of that app. - var bundleID: String -} -#endif diff --git a/StikDebugWidgets/Info.plist b/StikDebugWidgets/Info.plist deleted file mode 100644 index 0f118fb..0000000 --- a/StikDebugWidgets/Info.plist +++ /dev/null @@ -1,11 +0,0 @@ - - - - - NSExtension - - NSExtensionPointIdentifier - com.apple.widgetkit-extension - - - diff --git a/StikDebugWidgets/KeepAliveLiveActivityWidget.swift b/StikDebugWidgets/KeepAliveLiveActivityWidget.swift deleted file mode 100644 index ab95020..0000000 --- a/StikDebugWidgets/KeepAliveLiveActivityWidget.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// KeepAliveLiveActivityWidget.swift -// StikDebugWidgets -// -// Live Activity presentation for the background keep-alive session. Shows the -// held app in the Dynamic Island (compact, expanded, and minimal) and on the -// Lock Screen. -// - -import WidgetKit -import SwiftUI -import ActivityKit - -@available(iOS 16.1, *) -struct KeepAliveLiveActivityWidget: Widget { - var body: some WidgetConfiguration { - ActivityConfiguration(for: KeepAliveActivityAttributes.self) { context in - // Lock Screen / banner presentation. - KeepAliveLockScreenView(appName: context.state.appName) - .activityBackgroundTint(Color.black.opacity(0.55)) - .activitySystemActionForegroundColor(.green) - } dynamicIsland: { context in - DynamicIsland { - DynamicIslandExpandedRegion(.leading) { - Image(systemName: "bolt.circle.fill") - .font(.title2) - .foregroundStyle(.green) - .padding(.leading, 4) - } - DynamicIslandExpandedRegion(.trailing) { - Text("JIT") - .font(.caption.weight(.bold)) - .foregroundStyle(.green) - .padding(.trailing, 4) - } - DynamicIslandExpandedRegion(.center) { - Text(context.state.appName) - .font(.headline) - .lineLimit(1) - .minimumScaleFactor(0.7) - } - DynamicIslandExpandedRegion(.bottom) { - Text("Held alive in the background") - .font(.caption) - .foregroundStyle(.secondary) - } - } compactLeading: { - Image(systemName: "bolt.circle.fill") - .foregroundStyle(.green) - } compactTrailing: { - Text(context.state.appName) - .font(.caption2) - .lineLimit(1) - .frame(maxWidth: 64) - } minimal: { - Image(systemName: "bolt.circle.fill") - .foregroundStyle(.green) - } - .keylineTint(.green) - } - } -} - -@available(iOS 16.1, *) -private struct KeepAliveLockScreenView: View { - let appName: String - - var body: some View { - HStack(spacing: 12) { - Image(systemName: "bolt.circle.fill") - .font(.title) - .foregroundStyle(.green) - - VStack(alignment: .leading, spacing: 2) { - Text("Keeping \(appName) alive") - .font(.headline) - .lineLimit(1) - .minimumScaleFactor(0.7) - Text("Held in the background") - .font(.caption) - .foregroundStyle(.secondary) - } - - Spacer(minLength: 0) - } - .padding() - } -} diff --git a/StikDebugWidgets/StikDebugWidgetsBundle.swift b/StikDebugWidgets/StikDebugWidgetsBundle.swift deleted file mode 100644 index 2226e4b..0000000 --- a/StikDebugWidgets/StikDebugWidgetsBundle.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// StikDebugWidgetsBundle.swift -// StikDebugWidgets -// -// Widget extension bundle. Hosts the keep-alive Live Activity so it can be -// rendered in the Dynamic Island and on the Lock Screen. -// - -import WidgetKit -import SwiftUI - -@main -struct StikDebugWidgetsBundle: WidgetBundle { - var body: some Widget { - KeepAliveLiveActivityWidget() - } -}