didEnterBackground中发起的网络请求未总能被后端接收问题排查
问题根因分析
首先明确:didEnterBackgroundNotification本身触发逻辑是可靠的,你能通过日志确认方法被调用,就已经说明通知本身没有问题,异常表现来自iOS App生命周期的系统限制,以及你的代码疏漏。
两种场景的核心差异:
- 切换App进入后台:系统会为App预留3~5秒的时间执行当前未完成的同步/异步任务,之后才会将App进程挂起,你的网络请求足够在这个时间窗口内完成,因此可以正常发送到服务端。
- 上滑强制退出App:系统触发
didEnterBackgroundNotification之后,会立刻启动App终止流程,不会预留额外时间执行异步任务。你封装的Alamofire请求是异步执行的,还没等请求包发送出去,App进程就已经被系统销毁,因此请求无法到达服务端。
你代码的两处核心疏漏:
- 未申请后台执行权限:iOS允许App进入后台时申请最长约30秒的额外后台执行时间,用于完成未结束的关键任务,你未主动申请的情况下,强制退出场景下没有足够时间执行异步请求。
- 请求未设置completion回调:你调用
request(completion: nil)完全没有监听请求的完成状态,既无法确认请求是否执行成功,也无法在请求完成后主动通知系统任务结束。
修复方案
第一步:声明后台任务标识
在你的视图控制器类中添加后台任务标识属性:
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
第二步:调整didEnterBackground实现
修改方法逻辑,先申请后台任务再执行请求,请求完成后主动结束后台任务:
@objc func didEnterBackground() { // 申请后台执行权限 backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "SendLessonProgressTask") { [weak self] in // 后台任务超时回调,主动结束任务避免被系统强制杀进程 guard let self = self else { return } UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid } guard let course = self.focusedCourse.value else { // 没有需要处理的课程,直接结束后台任务 UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid return } switch course.status { case .finished: Worker.PutLessonProgress(lessonID: course.id, progress: Int(playTime.value)).request { [weak self] result in guard let self = self else { return } // 请求完成(无论成功失败)都主动结束后台任务 UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid } case .live, .upcoming, .todayUpcoming, .free: // 不需要处理的状态,直接结束后台任务 UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid break } }
补充说明
就算添加了后台任务逻辑,也无法保证极端场景下请求100%发送成功,比如系统资源极度紧张时,分配给你的后台执行时间可能远小于30秒。如果进度上报的可靠性要求很高,建议你增加本地持久化逻辑:发起请求前先把进度存在本地,请求成功后再删除本地记录,下次App启动时先检查有没有未上报的进度,补发请求即可。
内容的提问来源于stack exchange,提问作者Kex
相关产品推荐
相关产品推荐

