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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:53:05