feat(ios): add NWCActivityManager to start and update Live Activities
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
@@ -226,6 +226,11 @@
|
||||
NWCAUDIO0000000000000011 /* Fireplace.m4a in Resources */ = {isa = PBXBuildFile; fileRef = NWCAUDIO0000000000000015 /* Fireplace.m4a */; };
|
||||
NWCAUDIO0000000000000012 /* White Noise.m4a in Resources */ = {isa = PBXBuildFile; fileRef = NWCAUDIO0000000000000016 /* White Noise.m4a */; };
|
||||
NWCAUDIO0000000000000013 /* Gentle Rain.m4a in Resources */ = {isa = PBXBuildFile; fileRef = NWCAUDIO0000000000000017 /* Gentle Rain.m4a */; };
|
||||
NWCLA000000000000000001 /* NWCWidgetAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = NWCLA000000000000000007 /* NWCWidgetAttributes.swift */; };
|
||||
NWCLA000000000000000002 /* NWCWidgetBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = NWCLA000000000000000008 /* NWCWidgetBridge.swift */; };
|
||||
NWCLA000000000000000003 /* NWCActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = NWCLA000000000000000009 /* NWCActivityManager.swift */; };
|
||||
NWCLA000000000000000004 /* NWCWidgetBridgeImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = NWCLA000000000000000010 /* NWCWidgetBridgeImpl.swift */; };
|
||||
NWCLA000000000000000005 /* NWCWidget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = NWCLA000000000000000006 /* NWCWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -404,6 +409,13 @@
|
||||
remoteGlobalIDString = 358F4ED71D1E81A9004DF814;
|
||||
remoteInfo = RCTBlob;
|
||||
};
|
||||
NWCLA000000000000000019 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = NWCLA000000000000000011;
|
||||
remoteInfo = NWCWidget;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -414,6 +426,7 @@
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
5134B5512EB7C1B000A6AD03 /* ShareQR.appex in Embed Foundation Extensions */,
|
||||
NWCLA000000000000000005 /* NWCWidget.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -703,9 +716,14 @@
|
||||
FDE09436C88E4DCA980DA7DE /* zeus_illustration_6a.jpg */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = zeus_illustration_6a.jpg; path = ../assets/images/zeus_illustration_6a.jpg; sourceTree = "<group>"; };
|
||||
NWC1A2B3C4D5E6F700000002 /* NWCAudioKeepAlive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NWCAudioKeepAlive.m; path = zeus/NWCAudioKeepAlive.m; sourceTree = "<group>"; };
|
||||
NWC1A2B3C4D5E6F700000003 /* NWCAudioKeepAlive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NWCAudioKeepAlive.h; path = zeus/NWCAudioKeepAlive.h; sourceTree = "<group>"; };
|
||||
NWCAUDIO0000000000000015 /* Fireplace.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; name = Fireplace.m4a; path = zeus/Fireplace.m4a; sourceTree = "<group>"; };
|
||||
NWCAUDIO0000000000000016 /* White Noise.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; name = "White Noise.m4a"; path = "zeus/White Noise.m4a"; sourceTree = "<group>"; };
|
||||
NWCAUDIO0000000000000017 /* Gentle Rain.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Gentle Rain.m4a"; path = "zeus/Gentle Rain.m4a"; sourceTree = "<group>"; };
|
||||
NWCAUDIO0000000000000015 /* Fireplace.m4a */ = {isa = PBXFileReference; lastKnownFileType = "audio.x-m4a"; name = Fireplace.m4a; path = zeus/Fireplace.m4a; sourceTree = "<group>"; };
|
||||
NWCAUDIO0000000000000016 /* White Noise.m4a */ = {isa = PBXFileReference; lastKnownFileType = "audio.x-m4a"; name = "White Noise.m4a"; path = "zeus/White Noise.m4a"; sourceTree = "<group>"; };
|
||||
NWCAUDIO0000000000000017 /* Gentle Rain.m4a */ = {isa = PBXFileReference; lastKnownFileType = "audio.x-m4a"; name = "Gentle Rain.m4a"; path = "zeus/Gentle Rain.m4a"; sourceTree = "<group>"; };
|
||||
NWCLA000000000000000006 /* NWCWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NWCWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
NWCLA000000000000000007 /* NWCWidgetAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NWCWidgetAttributes.swift; path = NWCWidget/NWCWidgetAttributes.swift; sourceTree = "<group>"; };
|
||||
NWCLA000000000000000008 /* NWCWidgetBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NWCWidgetBridge.swift; path = NWCWidget/NWCWidgetBridge.swift; sourceTree = "<group>"; };
|
||||
NWCLA000000000000000009 /* NWCActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NWCActivityManager.swift; path = zeus/NWCActivityManager.swift; sourceTree = "<group>"; };
|
||||
NWCLA000000000000000010 /* NWCWidgetBridgeImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NWCWidgetBridgeImpl.swift; path = zeus/NWCWidgetBridgeImpl.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
@@ -716,6 +734,14 @@
|
||||
);
|
||||
target = 5134B5462EB7C1B000A6AD03 /* ShareQR */;
|
||||
};
|
||||
NWCLA000000000000000013 /* Exceptions for "NWCWidget" folder in "NWCWidget" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
NWCWidget.entitlements,
|
||||
);
|
||||
target = NWCLA000000000000000011 /* NWCWidget */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
@@ -727,6 +753,14 @@
|
||||
path = ShareQR;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
NWCLA000000000000000015 /* NWCWidget */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
NWCLA000000000000000013 /* Exceptions for "NWCWidget" folder in "NWCWidget" target */,
|
||||
);
|
||||
path = NWCWidget;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -778,6 +812,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
NWCLA00000000000000001B /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@@ -874,6 +915,10 @@
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
|
||||
NWC1A2B3C4D5E6F700000003 /* NWCAudioKeepAlive.h */,
|
||||
NWC1A2B3C4D5E6F700000002 /* NWCAudioKeepAlive.m */,
|
||||
NWCLA000000000000000009 /* NWCActivityManager.swift */,
|
||||
NWCLA000000000000000010 /* NWCWidgetBridgeImpl.swift */,
|
||||
NWCLA000000000000000007 /* NWCWidgetAttributes.swift */,
|
||||
NWCLA000000000000000008 /* NWCWidgetBridge.swift */,
|
||||
NWCAUDIO0000000000000015 /* Fireplace.m4a */,
|
||||
NWCAUDIO0000000000000016 /* White Noise.m4a */,
|
||||
NWCAUDIO0000000000000017 /* Gentle Rain.m4a */,
|
||||
@@ -982,6 +1027,7 @@
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
00E356EF1AD99517003FC87E /* zeusTests */,
|
||||
5134B5482EB7C1B000A6AD03 /* ShareQR */,
|
||||
NWCLA000000000000000015 /* NWCWidget */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
C313040DFF224D8A9EC46990 /* Resources */,
|
||||
@@ -1003,6 +1049,7 @@
|
||||
2D02E47B1E0B4A5D006451C7 /* zeus-tvOS.app */,
|
||||
2D02E4901E0B4A5D006451C7 /* zeus-tvOSTests.xctest */,
|
||||
5134B5472EB7C1B000A6AD03 /* ShareQR.appex */,
|
||||
NWCLA000000000000000006 /* NWCWidget.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -1318,6 +1365,7 @@
|
||||
);
|
||||
dependencies = (
|
||||
5134B5502EB7C1B000A6AD03 /* PBXTargetDependency */,
|
||||
NWCLA000000000000000020 /* PBXTargetDependency */,
|
||||
);
|
||||
name = zeus;
|
||||
productName = "Hello World";
|
||||
@@ -1380,6 +1428,26 @@
|
||||
productReference = 5134B5472EB7C1B000A6AD03 /* ShareQR.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
NWCLA000000000000000011 /* NWCWidget */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = NWCLA000000000000000016 /* Build configuration list for PBXNativeTarget "NWCWidget" */;
|
||||
buildPhases = (
|
||||
NWCLA000000000000000012 /* Sources */,
|
||||
NWCLA00000000000000001B /* Frameworks */,
|
||||
NWCLA000000000000000014 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
NWCLA000000000000000015 /* NWCWidget */,
|
||||
);
|
||||
name = NWCWidget;
|
||||
productName = NWCWidget;
|
||||
productReference = NWCLA000000000000000006 /* NWCWidget.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -1412,6 +1480,9 @@
|
||||
5134B5462EB7C1B000A6AD03 = {
|
||||
CreatedOnToolsVersion = 16.4;
|
||||
};
|
||||
NWCLA000000000000000011 = {
|
||||
CreatedOnToolsVersion = 16.4;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "zeus" */;
|
||||
@@ -1478,6 +1549,7 @@
|
||||
2D02E47A1E0B4A5D006451C7 /* zeus-tvOS */,
|
||||
2D02E48F1E0B4A5D006451C7 /* zeus-tvOSTests */,
|
||||
5134B5462EB7C1B000A6AD03 /* ShareQR */,
|
||||
NWCLA000000000000000011 /* NWCWidget */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -1850,6 +1922,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
NWCLA000000000000000014 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
@@ -1990,6 +2069,10 @@
|
||||
745388452A54C2B6000927CE /* LndMobileScheduledSync.swift in Sources */,
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||
NWC1A2B3C4D5E6F700000001 /* NWCAudioKeepAlive.m in Sources */,
|
||||
NWCLA000000000000000001 /* NWCWidgetAttributes.swift in Sources */,
|
||||
NWCLA000000000000000002 /* NWCWidgetBridge.swift in Sources */,
|
||||
NWCLA000000000000000003 /* NWCActivityManager.swift in Sources */,
|
||||
NWCLA000000000000000004 /* NWCWidgetBridgeImpl.swift in Sources */,
|
||||
745388442A54C2B6000927CE /* lightning.pb.swift in Sources */,
|
||||
74B637822A5A350B00750202 /* LncModule.mm in Sources */,
|
||||
74B637842A5A350B00750202 /* Callback.mm in Sources */,
|
||||
@@ -2025,6 +2108,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
NWCLA000000000000000012 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@@ -2043,6 +2133,11 @@
|
||||
target = 5134B5462EB7C1B000A6AD03 /* ShareQR */;
|
||||
targetProxy = 5134B54F2EB7C1B000A6AD03 /* PBXContainerItemProxy */;
|
||||
};
|
||||
NWCLA000000000000000020 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = NWCLA000000000000000011 /* NWCWidget */;
|
||||
targetProxy = NWCLA000000000000000019 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -2566,6 +2661,98 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
NWCLA000000000000000017 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = NWCWidget/NWCWidget.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 9TU7M3555F;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = NWCWidget/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Zeus NWC";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 13.1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.zeusln.zeus.NWCWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
NWCLA000000000000000018 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = NWCWidget/NWCWidget.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 9TU7M3555F;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = NWCWidget/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Zeus NWC";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 13.1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.zeusln.zeus.NWCWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -2623,6 +2810,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
NWCLA000000000000000016 /* Build configuration list for PBXNativeTarget "NWCWidget" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
NWCLA000000000000000017 /* Debug */,
|
||||
NWCLA000000000000000018 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
|
||||
+26
-1
@@ -50,6 +50,18 @@ static void ClearKeychainIfNecessary() {
|
||||
[RNNotifications startMonitorNotifications];
|
||||
ClearKeychainIfNecessary();
|
||||
|
||||
// Initialize NWCActivityManager early so the widget bridge is set up before
|
||||
// any Live Activity intent fires (intents run in the host-app process).
|
||||
if (@available(iOS 16.1, *)) {
|
||||
Class managerClass = NSClassFromString(@"NWCActivityManager");
|
||||
if (managerClass && [managerClass respondsToSelector:@selector(shared)]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[managerClass performSelector:@selector(shared)];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
@@ -86,7 +98,20 @@ static void ClearKeychainIfNecessary() {
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
NSLog(@"App will terminate");
|
||||
NSLog(@"App will terminate – stopping Live Activity");
|
||||
if (@available(iOS 16.1, *)) {
|
||||
Class managerClass = NSClassFromString(@"NWCActivityManager");
|
||||
if (managerClass && [managerClass respondsToSelector:@selector(shared)]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
id manager = [managerClass performSelector:@selector(shared)];
|
||||
#pragma clang diagnostic pop
|
||||
if ([manager respondsToSelector:@selector(stopActivity)]) {
|
||||
[manager performSelector:@selector(stopActivity)];
|
||||
}
|
||||
}
|
||||
}
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -128,6 +128,10 @@
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>NSSupportsLiveActivities</key>
|
||||
<true/>
|
||||
<key>NSSupportsLiveActivitiesFrequentUpdates</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import Foundation
|
||||
import ActivityKit
|
||||
|
||||
/// Manages the NWC Live Activity (Dynamic Island + lock-screen banner).
|
||||
@available(iOS 16.1, *)
|
||||
@objc final class NWCActivityManager: NSObject {
|
||||
|
||||
@objc static let shared = NWCActivityManager()
|
||||
|
||||
// Callbacks wired by NWCAudioKeepAlive.m so widget button taps reach the audio engine.
|
||||
@objc var onNextTrack: (() -> Void)?
|
||||
@objc var onPrevTrack: (() -> Void)?
|
||||
@objc var onToggleMute: (() -> Void)?
|
||||
@objc var onStop: (() -> Void)?
|
||||
|
||||
// Only adopt an existing activity that is still live (not ended/dismissed).
|
||||
// Orphaned activities from previous crashes are ended in init().
|
||||
private var activity: Activity<NWCLiveActivityAttributes>?
|
||||
|
||||
override private init() {
|
||||
super.init()
|
||||
NWCBridge = NWCWidgetBridgeImpl(manager: self)
|
||||
cleanupOrphanedActivities()
|
||||
}
|
||||
|
||||
// ─── ObjC entry points ────────────────────────────────────────────────────
|
||||
|
||||
@objc func startActivity(trackName: String, isMuted: Bool) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.startActivityOnMain(trackName: trackName, isMuted: isMuted)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func updateActivity(trackName: String?, isMuted: Bool) {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.updateActivityOnMain(trackName: trackName, isMuted: isMuted)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func stopActivity() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.stopActivityOnMain()
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Main-queue implementation ────────────────────────────────────────────
|
||||
|
||||
// 5-minute rolling staleDate – safety net if the app is killed mid-session.
|
||||
private var staleDate: Date { Date().addingTimeInterval(300) }
|
||||
|
||||
private func startActivityOnMain(trackName: String, isMuted: Bool) {
|
||||
let auth = ActivityAuthorizationInfo()
|
||||
guard auth.areActivitiesEnabled else {
|
||||
NSLog("[NWCActivity] Live Activities disabled – go to Settings → ZEUS → Live Activities")
|
||||
return
|
||||
}
|
||||
|
||||
// Re-use any existing live activity (active OR stale — both accept updates).
|
||||
if let existing = activity, isLive(existing) {
|
||||
NSLog("[NWCActivity] activity already active, updating instead")
|
||||
updateActivityOnMain(trackName: trackName, isMuted: isMuted)
|
||||
return
|
||||
}
|
||||
|
||||
let attrs = NWCLiveActivityAttributes(startedAt: .now)
|
||||
let state = NWCLiveActivityAttributes.ContentState(
|
||||
currentTrackName: trackName,
|
||||
isMuted: isMuted
|
||||
)
|
||||
|
||||
do {
|
||||
if #available(iOS 16.2, *) {
|
||||
let content = ActivityContent(state: state, staleDate: staleDate)
|
||||
activity = try Activity.request(attributes: attrs, content: content, pushType: .none)
|
||||
} else {
|
||||
activity = try Activity.request(attributes: attrs, contentState: state, pushType: .none)
|
||||
}
|
||||
NSLog("[NWCActivity] started – id=%@ track=%@", activity?.id ?? "?", trackName)
|
||||
} catch {
|
||||
NSLog("[NWCActivity] FAILED to start – %@ (%@)",
|
||||
error.localizedDescription, String(describing: error))
|
||||
}
|
||||
}
|
||||
|
||||
private func updateActivityOnMain(trackName: String?, isMuted: Bool) {
|
||||
guard let current = activity, isLive(current) else {
|
||||
NSLog("[NWCActivity] update skipped – no live activity")
|
||||
return
|
||||
}
|
||||
let state = NWCLiveActivityAttributes.ContentState(
|
||||
currentTrackName: trackName,
|
||||
isMuted: isMuted
|
||||
)
|
||||
Task {
|
||||
if #available(iOS 16.2, *) {
|
||||
// Rolling the staleDate forward keeps the activity alive during a long session.
|
||||
let content = ActivityContent(state: state, staleDate: staleDate)
|
||||
await current.update(content)
|
||||
} else {
|
||||
await current.update(using: state)
|
||||
}
|
||||
NSLog("[NWCActivity] updated – track=%@ muted=%d",
|
||||
trackName ?? "nil", isMuted ? 1 : 0)
|
||||
}
|
||||
}
|
||||
|
||||
private func stopActivityOnMain() {
|
||||
guard let current = activity else { return }
|
||||
activity = nil
|
||||
let finalState = NWCLiveActivityAttributes.ContentState(
|
||||
currentTrackName: nil,
|
||||
isMuted: false
|
||||
)
|
||||
Task {
|
||||
if #available(iOS 16.2, *) {
|
||||
let content = ActivityContent(state: finalState, staleDate: Date())
|
||||
await current.end(content, dismissalPolicy: .immediate)
|
||||
} else {
|
||||
await current.end(using: finalState, dismissalPolicy: .immediate)
|
||||
}
|
||||
NSLog("[NWCActivity] ended")
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// True when the activity can still be updated (active or stale but not ended/dismissed).
|
||||
private func isLive(_ act: Activity<NWCLiveActivityAttributes>) -> Bool {
|
||||
if act.activityState == .ended { return false }
|
||||
if #available(iOS 16.2, *) {
|
||||
if act.activityState == .dismissed { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/// End any activities left over from a previous session (e.g. a crash that
|
||||
/// prevented the normal stopActivity() call from completing).
|
||||
private func cleanupOrphanedActivities() {
|
||||
let orphans = Activity<NWCLiveActivityAttributes>.activities
|
||||
guard !orphans.isEmpty else { return }
|
||||
NSLog("[NWCActivity] cleaning up %d orphaned activit(y|ies)", orphans.count)
|
||||
Task {
|
||||
for orphan in orphans {
|
||||
if #available(iOS 16.2, *) {
|
||||
let content = ActivityContent(
|
||||
state: NWCLiveActivityAttributes.ContentState(
|
||||
currentTrackName: nil, isMuted: false),
|
||||
staleDate: Date()
|
||||
)
|
||||
await orphan.end(content, dismissalPolicy: .immediate)
|
||||
} else {
|
||||
await orphan.end(using: nil, dismissalPolicy: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import Foundation
|
||||
|
||||
/// Real bridge implementation compiled into the main zeus app target only.
|
||||
/// Routes Live Activity button taps back to NWCActivityManager's callback blocks
|
||||
/// (which NWCAudioKeepAlive.m registers at session start).
|
||||
@available(iOS 16.1, *)
|
||||
final class NWCWidgetBridgeImpl: NWCWidgetBridgeProtocol {
|
||||
|
||||
private weak var manager: NWCActivityManager?
|
||||
|
||||
init(manager: NWCActivityManager) {
|
||||
self.manager = manager
|
||||
}
|
||||
|
||||
func nextTrack() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.manager?.onNextTrack?()
|
||||
}
|
||||
}
|
||||
|
||||
func prevTrack() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.manager?.onPrevTrack?()
|
||||
}
|
||||
}
|
||||
|
||||
func toggleMute() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.manager?.onToggleMute?()
|
||||
}
|
||||
}
|
||||
|
||||
func stopNWC() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.manager?.onStop?()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user