iOS后台/终止状态下接收远程通知执行操作失败求助
解决iOS后台/终止状态下接收远程通知时发送设备位置的问题
咱们一步步来排查和解决这个问题,核心是要满足iOS后台唤醒和位置获取的双重要求:
1. 先确认远程通知的payload配置(最容易忽略的点)
后台/终止状态下,App要被通知唤醒执行代码,必须在APNs推送的payload里添加content-available: 1字段。这个字段是告诉系统:这个通知需要唤醒App处理后台任务。
示例推送payload(JSON格式):
{ "aps": { "content-available": 1, "alert": "请上报位置", // 可选,无此字段则为静默通知 "sound": "default" }, // 自定义字段,用来标记这是触发位置上报的通知 "need_location_report": true }
如果用第三方推送平台(比如极光、个推),要在推送设置里开启「后台唤醒」或「content-available」选项,确保这个字段被正确加入推送内容。
2. 检查App的后台模式与权限配置
在Xcode的Signing & Capabilities面板里,完成以下配置:
- 添加
Background Modes,并勾选两项:- Remote notifications:允许App被远程通知唤醒后台
- Location updates:允许App在后台获取位置
- 在Info.plist中添加位置权限描述:
NSLocationAlwaysAndWhenInUseUsageDescription:申请「始终允许」位置权限的提示文本(比如"需要在后台获取您的位置以完成上报")NSLocationWhenInUseUsageDescription:申请「使用时允许」的提示文本
同时,代码里必须请求始终允许的位置权限(仅「使用时允许」在后台无法获取位置):
let locationManager = CLLocationManager() locationManager.requestAlwaysAuthorization()
3. 针对不同App状态处理通知回调
(1)后台状态:正确使用didReceiveRemoteNotification
后台状态下,application(_:didReceiveRemoteNotification:fetchCompletionHandler:)会被调用,但要注意两个关键点:
- 必须在30秒内调用
completionHandler,否则系统会强制终止App - 启动后台任务延长执行时间,避免位置请求还没完成就被系统挂起
示例代码:
// 在AppDelegate里保存后台任务ID private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { // 校验是否是触发位置上报的通知 guard userInfo["need_location_report"] as? Bool == true else { completionHandler(.noData) return } // 启动后台任务 backgroundTaskID = application.beginBackgroundTask(withName: "LocationReportTask") { // 任务超时,结束后台任务 application.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid completionHandler(.failed) } // 配置位置管理器 let locationManager = CLLocationManager() locationManager.delegate = self locationManager.allowsBackgroundLocationUpdates = true // 必须开启 locationManager.pausesLocationUpdatesAutomatically = false // 防止系统自动暂停位置更新 locationManager.startUpdatingLocation() } // 实现CLLocationManagerDelegate处理位置回调 extension AppDelegate: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let latestLocation = locations.last else { return } // 发送位置到服务器的逻辑 uploadLocationToServer(location: latestLocation) { success in manager.stopUpdatingLocation() // 结束后台任务 if self.backgroundTaskID != .invalid { UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid } // 通知系统任务完成状态 completionHandler(success ? .newData : .failed) } } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { manager.stopUpdatingLocation() // 处理错误,结束任务 if self.backgroundTaskID != .invalid { UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid } completionHandler(.failed) } }
(2)终止状态:通过didFinishLaunchingWithOptions处理
当App完全被杀死(终止状态)时,远程通知会触发application(_:didFinishLaunchingWithOptions:),而非didReceiveRemoteNotification。需要在这里判断是否是通知唤醒的App:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 检查是否是远程通知唤醒App if let remoteNotification = launchOptions?[.remoteNotification] as? [AnyHashable: Any], remoteNotification["need_location_report"] as? Bool == true { var launchTaskID: UIBackgroundTaskIdentifier = .invalid launchTaskID = application.beginBackgroundTask(withName: "LaunchLocationReport") { application.endBackgroundTask(launchTaskID) launchTaskID = .invalid } // 配置位置管理器并开始获取位置 let locationManager = CLLocationManager() locationManager.delegate = self locationManager.allowsBackgroundLocationUpdates = true locationManager.pausesLocationUpdatesAutomatically = false locationManager.startUpdatingLocation() } return true }
4. 位置获取的关键细节
- 不要用
requestLocation():这个方法在后台可能不会触发回调,系统会优先处理前台任务。改用startUpdatingLocation(),获取到位置后立即调用stopUpdatingLocation()减少功耗。 - 确保
allowsBackgroundLocationUpdates = true:即使开启了后台模式,这个属性不设置为true,后台也无法获取位置。 - 处理权限异常:代码中要判断当前位置权限状态,如果用户关闭了权限,要给出提示或跳过上报逻辑。
5. 正确的测试方式
不要用Xcode直接运行测试后台/终止状态(Xcode附加调试时,App的后台行为和真实环境不一致):
- 用Xcode把App安装到设备上
- 断开设备与Xcode的连接
- 手动杀死App(终止状态)或切换到后台
- 用APNs测试工具发送包含
content-available:1的通知 - 查看设备Console日志(Xcode的
Window > Devices and Simulators中打开Console,过滤App名称),排查是否有后台唤醒或位置相关的错误日志。
内容的提问来源于stack exchange,提问作者Pooja Mishra
相关产品推荐
相关产品推荐

