iOS后台/关闭状态下通知检查函数无法运行的问题求助
后台/应用关闭时通知检查函数无法执行的问题排查与修复
我编写了一个检查数组新增通知的函数,前台运行正常,但后台或应用关闭时完全失效。已配置后台模式(Background Fetch、Remote Notifications、Background processing),在Info.plist中添加了后台任务标识符com.showfa.fetchNotifications,并导入BackgroundTasks框架,相关代码如下:
通知检查核心函数
func fetchNotifications() { guard let url = URL(string: "http://example.com") else { return } var request = URLRequest(url: url) request.setValue(accessTkn, forHTTPHeaderField: "Authorization") URLSession.shared.dataTask(with: request) { data, _, error in if let error = error { print("Error fetching data: \(error)") return } if let data = data { do { let decoder = JSONDecoder() let fetchedNotifications = try decoder.decode([Notification].self, from: data) DispatchQueue.main.async { self.notifications = fetchedNotifications if let decodedNotifications = self.loadNotificationsFromUserDefaults() { let newValue = self.notifications.filter { newNoti in !decodedNotifications.contains { newData in newNoti.message == newData.message } } if newValue.isEmpty { print("1") } else { for newNoti in newValue { self.notiCat = newNoti.category self.notiMess = newNoti.message self.sendPushNotificationIfNeeded(category: self.notiCat, message: self.notiMess) } } } else { print("E1") } // Save notifications to UserDefaults self.saveNotificationsToUserDefaults(notifications: self.notifications) } } catch { print("Error decoding data: \(error)") } } }.resume() }
AppDelegate相关代码
@main struct CabsPointApp: App { let persistenceController = PersistenceController.shared @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate init() { // Request background task processing identifier BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.showfa.fetchNotifications", using: nil) { task in if let appDelegate = UIApplication.shared.delegate as? AppDelegate, let appRefreshTask = task as? BGAppRefreshTask { appDelegate.handleAppRefreshTask(task: appRefreshTask) } } } var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { var notificationViewModel = NotificationViewModel() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in if granted { print("Notification authorization granted") } else { print("Notification authorization denied") } } scheduleBackgroundTask() return true } } extension AppDelegate { // Handle notification when the app is in the foreground func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert, .sound, .badge]) } // Handle notification when the user taps on it func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // Handle the user's action here completionHandler() } func scheduleBackgroundTask() { let request = BGAppRefreshTaskRequest(identifier: "com.showfa.fetchNotifications") request.earliestBeginDate = Date(timeIntervalSinceNow: 3 * 1) // Start task 3 seconds from now do { try BGTaskScheduler.shared.submit(request) print("Scheduled background task for fetching notifications") } catch { print("Unable to schedule background task: \(error)") } } func handleAppRefreshTask(task: BGAppRefreshTask) { let queue = OperationQueue() queue.maxConcurrentOperationCount = 1 let operation = BlockOperation { self.notificationViewModel.startBackgroundTask() self.scheduleBackgroundTask() task.setTaskCompleted(success: true) } queue.addOperation(operation) } }
核心问题与修复方案
1. 后台任务生命周期管理错误
当前handleAppRefreshTask在异步请求未完成时就标记任务完成,系统会直接终止后续操作。
- 修复:给异步函数添加完成回调,确保所有逻辑执行完毕后再标记任务完成,同时设置过期处理避免系统强制终止。
更新NotificationViewModel的相关函数:
func startBackgroundTask(completion: @escaping () -> Void) { fetchNotifications(completion: completion) } func fetchNotifications(completion: @escaping () -> Void) { guard let url = URL(string: "http://example.com") else { completion() return } var request = URLRequest(url: url) request.setValue(accessTkn, forHTTPHeaderField: "Authorization") URLSession.shared.dataTask(with: request) { data, _, error in defer { completion() } if let error = error { print("Error fetching data: \(error)") return } if let data = data { do { let decoder = JSONDecoder() let fetchedNotifications = try decoder.decode([Notification].self, from: data) DispatchQueue.main.sync { self.notifications = fetchedNotifications if let decodedNotifications = self.loadNotificationsFromUserDefaults() { let newValue = self.notifications.filter { newNoti in !decodedNotifications.contains { $0.message == newNoti.message } } newValue.forEach { self.sendPushNotificationIfNeeded(category: $0.category, message: $0.message) } } self.saveNotificationsToUserDefaults(notifications: self.notifications) } } catch { print("Error decoding data: \(error)") } } }.resume() }
修改handleAppRefreshTask:
func handleAppRefreshTask(task: BGAppRefreshTask) { task.expirationHandler = { task.setTaskCompleted(success: false) print("Background task expired before completion") } let queue = OperationQueue() queue.maxConcurrentOperationCount = 1 let operation = BlockOperation { let semaphore = DispatchSemaphore(value: 0) self.notificationViewModel.startBackgroundTask { semaphore.signal() } semaphore.wait() self.scheduleBackgroundTask() task.setTaskCompleted(success: true) } queue.addOperation(operation) }
2. 后台任务调度间隔不合理
iOS会忽略过短的调度间隔(如3秒),实际不会触发任务。
- 修复:设置合理的间隔(如15分钟),系统会根据用户使用习惯优化触发时机:
func scheduleBackgroundTask() { let request = BGAppRefreshTaskRequest(identifier: "com.showfa.fetchNotifications") request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 15) do { try BGTaskScheduler.shared.submit(request) print("Scheduled background task for fetching notifications") } catch { print("Unable to schedule background task: \(error)") } }
3. 应用关闭时的任务限制
应用完全关闭后,BGAppRefreshTask不会触发,需依赖APNs静默推送唤醒应用。
- 补充方案:服务器发送带
content-available: 1的静默推送,在AppDelegate中处理:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { notificationViewModel.fetchNotifications { completionHandler(.newData) } }
4. 其他细节优化
- 确保
Info.plist的BGTaskSchedulerPermittedIdentifiers数组包含com.showfa.fetchNotifications; - 保证UserDefaults的读写操作都在主线程,避免线程安全问题;
- 确认Xcode的Signing & Capabilities中已正确勾选Background Modes下的Background fetch和Remote notifications。
内容的提问来源于stack exchange,提问作者CodeMan
相关产品推荐
相关产品推荐

