iPhone重启后Live Activity未终止的问题解决咨询
问题:手机重启后ActivityKit的Live Activity仍保持活跃,如何在重启/电量耗尽时终止它
我用ActivityKit给德州扑克实时对局做开始时间和时长计时,后台运行正常,应用终止时Live Activity会结束,这没问题,计数器会在应用其他位置继续追踪。但手机重启时如果Live Activity处于活跃状态,重启后它还会保持活跃,导致应用重新打开时出问题。希望在手机重启或电量耗尽时终止Live Activity,求正确处理方式。
TimerViewModel类核心代码
class TimerViewModel: ObservableObject { private var timer: Timer? @Published var liveSessionStartTime: Date? @Published var liveSessionTimer: String = "00:00" @Published var activity: Activity<LiveSessionWidgetAttributes>? = nil @Published var reBuyAmount: String = "" @Published var initialBuyInAmount: String = "" @Published var totalRebuys: [Int] = [] var totalBuyInForLiveSession: Int { (Int(initialBuyInAmount) ?? 0) + rebuyTotalForSession } var rebuyTotalForSession: Int { return totalRebuys.reduce(0,+) } static var isCounting: Bool { UserDefaults.standard.object(forKey: "liveSessionStartTime") != nil } init() { NotificationCenter.default.addObserver(self, selector: #selector(appDidResume), name: UIApplication.willEnterForegroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive), name: UIApplication.willResignActiveNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(applicationWillTerminate), name: UIApplication.willTerminateNotification, object: nil) // 尝试从UserDefaults恢复liveSessionStartTime guard let startTime = UserDefaults.standard.object(forKey: "liveSessionStartTime") as? Date else { print("No Live Session start time found.") return } liveSessionStartTime = startTime updateElapsedTime() startUpdatingTimer() initialBuyInAmount = UserDefaults.standard.string(forKey: "initialBuyInAmount") ?? "" totalRebuys = UserDefaults.standard.array(forKey: "totalRebuys") as? [Int] ?? [] } func startSession() { let now = Date() liveSessionStartTime = now UserDefaults.standard.set(now, forKey: "liveSessionStartTime") UserDefaults.standard.set(initialBuyInAmount, forKey: "initialBuyInAmount") UserDefaults.standard.set(totalRebuys, forKey: "totalRebuys") startUpdatingTimer() scheduleUserNotification() if ActivityAuthorizationInfo().areActivitiesEnabled { let attributes = LiveSessionWidgetAttributes(eventDescription: "Live Session") let state = LiveSessionWidgetAttributes.TimerStatus(startTime: now, elapsedTime: self.liveSessionTimer) do { activity = try Activity<LiveSessionWidgetAttributes>.request(attributes: attributes, content: .init(state: state, staleDate: nil), pushType: nil) } catch { print("Error: \(error)") } } }
手动停止会话的代码(正常工作)
func stopTimer() { timer?.invalidate() UserDefaults.standard.removeObject(forKey: "liveSessionStartTime") UserDefaults.standard.removeObject(forKey: "initialBuyInAmount") UserDefaults.standard.removeObject(forKey: "totalRebuys") // 结束Activity Task { await Activity<LiveSessionWidgetAttributes>.activities.first?.end(activity?.content, dismissalPolicy: .immediate) } cancelUserNotifications() }
原以为能处理重启的代码(无效)
func endActivityKit() { print("Ending Live Activities") let semaphore = DispatchSemaphore(value: 0) Task { for activity in Activity<LiveSessionWidgetAttributes>.activities { print("Ending Live Activity: \(activity.id)") await activity.end(nil, dismissalPolicy: .immediate) } semaphore.signal() } semaphore.wait() } @objc func applicationWillTerminate(_ application: UIApplication) { print("Application will terminate...") endActivityKit() }
解决方案
1. 利用App启动/前台检查处理重启场景
手机重启后系统不会触发UIApplication.willTerminateNotification,所以要在App进入前台时,对比Live Activity和本地存储的会话状态,不匹配就终止Activity:
实现你已注册的appDidResume方法:
@objc func appDidResume() { // 检查本地是否存在活跃会话 let hasActiveSession = UserDefaults.standard.object(forKey: "liveSessionStartTime") != nil // 异步处理Live Activity状态 Task { let activities = Activity<LiveSessionWidgetAttributes>.activities for activity in activities { if !hasActiveSession { // 本地无活跃会话,直接终止残留的Activity await activity.end(nil, dismissalPolicy: .immediate) } else { // 本地有活跃会话,同步Activity的计时状态到最新值 let startTime = UserDefaults.standard.object(forKey: "liveSessionStartTime") as! Date let elapsedSeconds = Date().timeIntervalSince(startTime) // 替换成你自己的时间格式化逻辑,转成"HH:MM"格式 let formattedElapsed = String(format: "%02d:%02d", Int(elapsedSeconds)/3600, Int(elapsedSeconds)%3600/60) let updatedState = LiveSessionWidgetAttributes.TimerStatus(startTime: startTime, elapsedTime: formattedElapsed) await activity.update(ActivityContent(state: updatedState, staleDate: nil)) } } } }
2. 优化应用终止时的处理逻辑
你原代码用信号量阻塞主线程,可能导致系统在终止前无法完成异步操作。改用async/await的正确写法,避免阻塞:
@objc func applicationWillTerminate(_ application: UIApplication) { print("Application will terminate...") Task { await endActivityKit() } } func endActivityKit() async { print("Ending Live Activities") for activity in Activity<LiveSessionWidgetAttributes>.activities { print("Ending Live Activity: \(activity.id)") await activity.end(nil, dismissalPolicy: .immediate) } }
3. 给Live Activity设置过期时间(可选)
创建Activity时设置staleDate,超过该时间后Activity会自动进入 stale 状态,可在Widget中展示为已结束样式:
// 在startSession方法中修改content参数 // 例如设置24小时后过期 let staleDate = Date().addingTimeInterval(86400) activity = try Activity<LiveSessionWidgetAttributes>.request(attributes: attributes, content: .init(state: state, staleDate: staleDate), pushType: nil)
关键逻辑说明
- 手机重启后,系统会保留Live Activity,但本地存储的会话状态是判断Activity是否有效的唯一依据:本地无活跃会话则终止残留Activity,有则同步最新状态。
- 应用终止时不要阻塞主线程,用异步
async/await让系统有足够时间完成Activity终止操作。
内容的提问来源于stack exchange,提问作者comexbackkid
相关产品推荐
相关产品推荐

