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

iOS Swift实现后台每小时调用Alamofire接口并推送本地通知

解决方案:iOS后台/关闭状态下周期性获取数据并推送通知

核心问题分析

你当前使用UIApplication.willResignActiveNotification仅能在APP即将退至后台时触发一次请求,无法实现周期性执行;且APP完全关闭后,沙盒内的代码完全停止运行,常规本地通知触发无法唤醒APP执行网络请求。iOS对后台行为有严格限制,必须依赖系统提供的合法后台机制才能实现需求。


方案1:后台刷新任务(Background App Refresh)- 处理APP后台状态

该机制允许系统在后台唤醒APP,执行短时间的数据获取任务,系统会根据用户使用习惯、设备状态调度执行(无法保证严格每小时,但可设置最小间隔)。

步骤1:开启后台刷新权限

  1. 在Info.plist中添加键UIBackgroundModes,添加子项fetch;
  2. 在Xcode项目的Signing & Capabilities中,开启Background Modes并勾选Background fetch。

步骤2:实现后台刷新回调

在AppDelegate中实现以下方法,替换原有的通知监听逻辑:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 设置后台刷新最小间隔(单位:秒),这里设为3600小时
    application.setMinimumBackgroundFetchInterval(3600)
    // 请求通知权限
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
        if granted {
            DispatchQueue.main.async {
                application.registerForRemoteNotifications()
            }
        }
    }
    return true
}

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // 调用数据获取方法,完成后通知系统
    getCustomerOrders { hasNewData in
        completionHandler(hasNewData ? .newData : .noData)
    }
}

步骤3:重构数据获取方法

将原有的getCustomerOrders改为带完成回调的异步方法,避免阻塞后台任务:

func getCustomerOrders(completion: @escaping (Bool) -> Void) {
    let headers = [
        "accept": "application/json",
        "Username": "info@bse.com.cy",
        "Password": "1234"
    ]
    
    Alamofire.request("https://leathermaster.bse.com.cy/api/getcompletedorders", method: .get, headers: headers)
        .responseJSON { response in
            switch response.result {
            case .success:
                guard let data = response.data else {
                    completion(false)
                    return
                }
                do {
                    let orders = try JSONDecoder().decode([OrderComplete].self, from: data)
                    let preferences = UserDefaults.standard
                    guard let userPhone = preferences.string(forKey: "phone") else {
                        completion(false)
                        return
                    }
                    
                    // 筛选当前用户的有效订单
                    let userOrders = orders.filter { $0.PhoneNumber == userPhone }
                    guard !userOrders.isEmpty else {
                        completion(false)
                        return
                    }
                    
                    // 推送本地通知(找到符合条件的订单立即推送)
                    let center = UNUserNotificationCenter.current()
                    for order in userOrders {
                        let content = UNMutableNotificationContent()
                        let userName = "\(preferences.string(forKey: "firstname") ?? "") \(preferences.string(forKey: "surname") ?? "")"
                        content.title = "\(userName) your order with \(order.TransactionCode) is ready for pickup"
                        let cleanDate = order.TransactionDate.date.replacingOccurrences(of: ".000000", with: "")
                        content.body = "\(order.TransactionStatus) | Date: \(cleanDate)"
                        content.sound = .default
                        content.userInfo = ["transactionCode": order.TransactionCode]
                        
                        // 立即触发通知
                        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
                        let request = UNNotificationRequest(identifier: "order_\(order.TransactionCode)", content: content, trigger: trigger)
                        center.add(request) { error in
                            if let error = error {
                                print("Notification error: \(error.localizedDescription)")
                            }
                        }
                    }
                    completion(true)
                } catch {
                    print("Decode error: \(error.localizedDescription)")
                    completion(false)
                }
            case .failure(let error):
                print("Request error: \(error.localizedDescription)")
                completion(false)
            }
        }
}

方案2:静默远程通知 - 处理APP完全关闭状态

iOS中APP完全关闭后,无法通过本地机制唤醒,必须依赖静默远程通知(Silent Push)。服务器定时发送静默通知,系统会唤醒APP在后台执行代码,完成数据检查和通知推送。

步骤1:配置静默通知Payload

服务器发送的推送Payload需包含content-available: 1,示例:

{
    "aps": {
        "content-available": 1
    },
    "type": "order_check"
}

注意:静默通知不能包含alert、sound等可见内容,否则会变成普通推送;若需要同时展示通知,可添加alert字段并保留content-available:1。

步骤2:处理静默通知回调

在AppDelegate中实现以下方法:

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // 判断是否为订单检查类型的静默通知
    if userInfo["type"] as? String == "order_check" {
        getCustomerOrders { hasNewData in
            completionHandler(hasNewData ? .newData : .noData)
        }
    } else {
        completionHandler(.noData)
    }
}

关键注意事项

  • 后台刷新调度限制:系统不会严格按照设置的3600秒执行,会根据用户使用频率、电池电量等因素调整,低电量或长时间未使用APP时可能暂停。
  • 后台任务时长限制:所有后台任务(包括后台刷新、静默通知唤醒)的执行时间约为30秒,需确保网络请求高效完成。
  • 权限依赖:用户必须允许APP的「通知权限」和「后台刷新权限」,否则机制无法生效。
  • 避免重复通知:创建本地通知时使用唯一标识符(如订单号),避免重复添加相同通知。

内容的提问来源于stack exchange,提问作者Dimitra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 06:45:22