如何检测用户通过设置撤销HealthKit权限并做出响应?
解决HealthKit权限撤销后的崩溃与权限检测问题
问题场景
使用Apple HealthKit构建应用时,通过SwiftUI的.onAppear在View加载时请求睡眠数据读写权限,正常情况可获取数据,但用户通过系统设置撤销权限后,应用直接崩溃。现有实现代码如下:
View代码
@State var authorized: Bool = false @State var initTextContent: String = "" var body: some View { Text("\(initTextContent)") .onAppear { healthstore.getHealthAuthorization(completionAuth: { success in authorized = success if self.authorized == true { healthstore.getSleepMetrics(startDate: sevendaysAgo, endDate: sevendaysAfter, completion: sleepDictAssign) } else { initTextContent = "Required Authorization Not Provided" } }) } }
HealthStore类代码
class HealthStoreManager { let healthStore = HKHealthStore() private let sleepSampleType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis)! func getHealthAuthorization(completionAuth: @escaping (Bool) -> Void) { healthStore.requestAuthorization(toShare: [sleepSampleType], read: [sleepSampleType]) { (success, error) in if let error = error { // 原代码使用fatalError直接崩溃 fatalError("*** An error occurred \(error.localizedDescription) ***") } else { completionAuth(success) } } } // 假设存在getSleepMetrics方法 func getSleepMetrics(startDate: Date, endDate: Date, completion: @escaping ([String: Any]) -> Void) { // 数据获取逻辑 } }
核心问题分析
- 崩溃原因:
fatalError会直接终止应用,当用户撤销权限后,调用requestAuthorization会返回权限相关错误,触发fatalError导致崩溃。 - 权限请求限制:用户明确拒绝或撤销权限后,
requestAuthorization不会再次弹出权限请求弹窗,必须引导用户到系统设置手动开启。
解决方案
1. 移除致命错误,优雅处理权限错误
修改getHealthAuthorization方法,去掉fatalError,改为通过回调传递错误或状态:
func getHealthAuthorization(completionAuth: @escaping (Bool, Error?) -> Void) { healthStore.requestAuthorization(toShare: [sleepSampleType], read: [sleepSampleType]) { (success, error) in completionAuth(success, error) } }
2. 主动检测当前权限状态
在请求权限前,先检查当前的权限状态,避免无效的请求操作:
func checkSleepAuthorizationStatus() -> HKAuthorizationStatus { return healthStore.authorizationStatus(for: sleepSampleType) }
3. 修改View逻辑,处理权限撤销场景
在.onAppear中先检查权限状态,再决定后续操作,同时处理权限被拒绝的引导逻辑:
@State var authorized: Bool = false @State var initTextContent: String = "" @State var showSettingsAlert = false var body: some View { Text("\(initTextContent)") .onAppear { checkAndRequestAuthorization() } .alert("需要权限", isPresented: $showSettingsAlert) { Button("取消", role: .cancel) {} Button("去设置") { guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return } UIApplication.shared.open(settingsURL) } } } private func checkAndRequestAuthorization() { let status = healthstore.checkSleepAuthorizationStatus() switch status { case .sharingAuthorized: authorized = true healthstore.getSleepMetrics(startDate: sevendaysAgo, endDate: sevendaysAfter, completion: sleepDictAssign) case .notDetermined: // 用户还未选择权限,发起请求 healthstore.getHealthAuthorization(completionAuth: { [weak self] success, error in guard let self = self else { return } if success { self.authorized = true self.healthstore.getSleepMetrics(startDate: self.sevendaysAgo, endDate: self.sevendaysAfter, completion: self.sleepDictAssign) } else { self.initTextContent = "未获得必要权限" self.showSettingsAlert = true } }) case .sharingDenied, .unknown: // 用户已拒绝或撤销权限,引导去设置 initTextContent = "权限已被拒绝" showSettingsAlert = true @unknown default: initTextContent = "无法获取权限状态" } }
4. 确保数据获取方法的安全校验
在getSleepMetrics中增加权限校验,避免无权限时执行数据获取逻辑:
func getSleepMetrics(startDate: Date, endDate: Date, completion: @escaping ([String: Any]) -> Void) { let status = checkSleepAuthorizationStatus() guard status == .sharingAuthorized else { completion([:]) // 或传递错误信息 return } // 原数据获取逻辑 }
关键说明
- 用户拒绝权限后,系统不会再允许应用弹出权限请求框,必须引导用户到系统设置手动开启。
- 每次访问HealthKit数据前,都应该先检查权限状态,避免因权限变化导致崩溃。
内容的提问来源于stack exchange,提问作者Zo Ahmed
相关产品推荐
相关产品推荐

