You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 04:40:53