当应用程序被终止时,您可以通过以下步骤使用远程推送通知来更新实时活动。它与常规远程推送通知非常相似,但有一些变化:
1-您需要配置请求标头:
{APNS_TOPIC} 应该是您的应用程序包 ID
2- 您需要在有效负载中发送以下内容:
{
"aps":{
"timestamp":1673300490,
"event":"update",
"content-state":{
"progress": "10"
},
"alert":{
"title":"Update",
"body":"My Live Activity has been updated remotely" }
}
}
3- 要结束实时活动,您应该将“update”替换为“end”,并在其下方添加一个名为“dismissal-date”的新参数,该参数应为纪元时间。请注意,如果您输入过去的时间,它将立即被销毁。
就是这样!从您的服务器发送此推送通知后,您的实时活动应该相应更新。
就我个人而言,我想避免来自服务器的推送通知。我的解决方案涉及一些文件。我正在使用 React Native,但由于这是纯粹的 iOS 代码,因此该解决方案仍然适用。
注意:无论出于何种原因,在 XCode 中,记录器仅在每次应用程序初始化时(点击 XCode 中的播放按钮)在
applicationWillTerminate
中触发一次,但实际上每次用户重新打开应用程序并滑开时,此代码都会触发。
请记住,如果应用程序正在运行并且用户打开多应用程序查看器并从那里终止应用程序,则此功能是可靠的。如果应用程序已因内存问题或看门狗而被 Apple 系统暂停,则该行为可能不可靠。
Apple 开发者文档根本没有帮助。
applicationWillTerminate
函数第 1 步:(仅限 React Native):在
LiveActivityModule.m
中确保您的 LiveActivity 已正确声明。
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
// Important to note that the name "LiveActivity" was the name I declared as an Objective C module in my LiveActivityModule.swift file. It ensures the class can be referenced in react native
@interface RCT_EXTERN_MODULE(LiveActivity, NSObject)
RCT_EXTERN_METHOD(startActivity)
RCT_EXTERN_METHOD(updateActivity: (NSString *) name)
RCT_EXTERN_METHOD(endActivity)
// I don't think this is mandatory
+ (BOOL)requiresMainQueueSetup
{
return NO;
}
@end
第 2 步和第 3 步:在
AppDelegate.mm
中,创建应用程序委托 applicationWillTerminate 函数并引用 LiveActivity EndActivity 函数。
// other imports
#import <os/log.h>
// (React Native) You DO NOT need your bridging header to be imported, just the entire folder name of your app with '-Swift.h'. NOT the file name of the file that holds the EndActivity function
#import <AppName-Swift.h>
// I like to have a logger in this file, not mandatory
// Make sure you create the logger object in EACH FUNCTION BODY
os_log_t logger = nil;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// other initialization
logger = os_log_create("com.bundle.id", "AppDelegate");
os_log(logger, "In AppDelegate Initializing the app");
// final stuff you want to run
}
- (void)applicationWillTerminate:(UIApplication *)application {
logger = os_log_create("com.bundle.id", "AppDelegate");
os_log(logger, "App terminated! Good bye!");
// Create an instance of the LiveActivityModule class
if (@available(iOS 16.2, *)) {
os_log(logger, "Starting call to LiveActivity! Good bye!");
LiveActivity *liveActivityModule = [[LiveActivity alloc] init];
// Call the endActivity method
[liveActivityModule endActivity];
} else {
// Fallback on earlier versions
}
}
@end
步骤 4. 修改 Swift 代码中的结束活动函数,以确保即使应用程序突然被终止,任务也能完成。这是在我名为
LiveActivityModule.swift
的文件中
import SwiftUI
import ActivityKit
import OSLog
@available(iOS 16.2, *)
// Declaring this LiveActivity as an objective-c function
@objc(LiveActivity)
class LiveActivityModule: NSObject {
private var content: ActivityContent<LiveAttributes.ContentState>?
let logger = Logger(subsystem: "com.bundle.id", category: "dynamicIsland")
@objc(startActivity)
func startActivity() {
// other code
logger.debug("Starting activity")
}
@objc(updateActivity:)
func updateActivity(name: String) {
// other code
logger.debug("Updating activity")
}
@objc(endActivity)
func endActivity() {
logger.debug("Ending Live Activities")
// You need to add this Sephamore code. This way the Task will actually run
let semaphore = DispatchSemaphore(value: 0)
Task {
logger.debug("We're in that task before the for loop")
let updatedDeliveryStatus = LiveAttributes.ContentState(driverName: "TIM 👨🏻🍳")
for activity in Activity<LiveAttributes>.activities{
await activity.update(using: updatedDeliveryStatus)
}
semaphore.signal()
}
semaphore.wait()
}
}