SwiftUI WatchOS应用后台运行求助:放下手腕即被暂停
Apple Watch应用放下手腕后暂停运行的解决方案
核心问题分析
WKExtendedRuntimeSession生命周期管理错误:用@State存储会话和代理会导致对象被意外重置,无法维持持久的后台会话。- Core Motion后台权限未正确配置:缺少必要的权限声明和后台模式设置,手腕休眠时运动更新会被系统暂停。
- 会话状态与Core Motion运行未关联:会话失效或未激活时,运动数据更新无法持续执行。
具体修复步骤
1. 重构会话管理,用单例维护WKExtendedRuntimeSession
将会话管理从视图级@State移出,改用全局单例确保生命周期稳定:
import SwiftUI import WatchKit class ExtendedSessionManager: NSObject, ObservableObject, WKExtendedRuntimeSessionDelegate { static let shared = ExtendedSessionManager() private(set) var session: WKExtendedRuntimeSession? private override init() { super.init() } func startSession() { guard session == nil || session!.state != .running else { return } session = WKExtendedRuntimeSession() session?.delegate = self session?.start() } // MARK: - WKExtendedRuntimeSessionDelegate func extendedRuntimeSession(_ extendedRuntimeSession: WKExtendedRuntimeSession, didInvalidateWith reason: WKExtendedRuntimeSessionInvalidationReason, error: Error?) { print("会话失效:\(reason.rawValue) 错误:\(error?.localizedDescription ?? "无")") session = nil // 会话失效后延迟重试启动 DispatchQueue.global().asyncAfter(deadline: .now() + 5) { [weak self] in self?.startSession() } } func extendedRuntimeSessionDidStart(_ extendedRuntimeSession: WKExtendedRuntimeSession) { print("会话已启动") } func extendedRuntimeSessionWillExpire(_ extendedRuntimeSession: WKExtendedRuntimeSession) { print("会话即将过期") } } @main struct NoTouchLipAppApp: App { @StateObject private var sessionManager = ExtendedSessionManager.shared var body: some Scene { WindowGroup { ContentView() .onAppear { sessionManager.startSession() } } } }
2. 更新MotionManager,关联会话状态并配置权限
修改运动管理器,确保在会话活跃时启动更新,并添加权限检查逻辑:
import SwiftUI import CoreMotion import WatchKit class MotionManager: ObservableObject { private let motionManager = CMMotionManager() private let sessionManager = ExtendedSessionManager.shared @Published var x = 0.0 @Published var y = 0.0 @Published var z = 0.0 init() { checkMotionAuthorization() // 监听会话状态变化,同步启停运动更新 NotificationCenter.default.addObserver(forName: .WKExtendedRuntimeSessionDidStart, object: nil, queue: .main) { [weak self] _ in self?.startMotionUpdates() } NotificationCenter.default.addObserver(forName: .WKExtendedRuntimeSessionWillExpire, object: nil, queue: .main) { [weak self] _ in self?.stopMotionUpdates() } } private func checkMotionAuthorization() { switch CMMotionManager.authorizationStatus() { case .authorized: startMotionUpdates() case .notDetermined: CMMotionManager.requestAuthorization() { [weak self] granted in if granted { DispatchQueue.main.async { self?.startMotionUpdates() } } } default: print("未获得运动权限") } } private func startMotionUpdates() { guard motionManager.isDeviceMotionAvailable, !motionManager.isDeviceMotionActive else { return } motionManager.deviceMotionUpdateInterval = 1/2 // 用后台队列处理运动数据,避免阻塞主线程 motionManager.startDeviceMotionUpdates(to: OperationQueue()) { [weak self] data, error in guard let motion = data?.attitude else { return } DispatchQueue.main.async { self?.x = motion.roll self?.y = motion.pitch self?.z = motion.yaw if motion.roll < 0 && motion.pitch < 0 { WKInterfaceDevice.current().play(.click) } } } } private func stopMotionUpdates() { if motionManager.isDeviceMotionActive { motionManager.stopDeviceMotionUpdates() } } deinit { NotificationCenter.default.removeObserver(self) stopMotionUpdates() } } struct ContentView: View { @StateObject private var motion = MotionManager() var body: some View { VStack{ Text(String(format: "Roll: %.2f", motion.x)) Text(String(format: "Pitch: %.2f", motion.y)) Text(String(format: "Yaw: %.2f", motion.z)) } } }
3. 配置项目权限与后台模式
Info.plist添加权限声明:
添加NSMotionUsageDescription键,值填写权限申请说明(例如:"需要访问运动数据以检测手部动作,纠正触碰嘴唇的习惯")。开启后台模式:
在Xcode中选中watchOS目标 →Signing & Capabilities→ 添加Background Modes→ 勾选Motion & Fitness Updates。
关键说明
WKExtendedRuntimeSession有运行时长限制(通常最长约30分钟),到期后需重新启动。- 用后台队列处理运动数据,既能避免阻塞UI,也能提升后台运行稳定性。
- 必须完成权限申请,否则Core Motion无法在后台获取数据。
内容的提问来源于stack exchange,提问作者HanyPie
相关产品推荐
相关产品推荐

