2、Background Modes Capability:如何在Xcode中开启后台模式、Info.plist配置详解、常见的后台模式类型

好,咱们直接进入正题。后台模式这东西,说简单也简单,说复杂也复杂。我刚开始接触iOS后台开发时,第一反应就是:不就是勾个开关吗?结果后来踩了不少坑。今天我就把这块掰开揉碎了讲给你听。

2.1 如何在Xcode中开启后台模式

其实操作很简单,但很多人容易忽略一个关键点。来,我带你走一遍流程。

  1. 选中项目Target:在Xcode左侧导航栏里,点一下你的项目文件,然后找到对应的Target。
  2. 进入Signing & Capabilities:顶部选项卡里,点「Signing & Capabilities」。
  3. 添加Capability:点击左上角的「+ Capability」按钮,在弹出的搜索框里输入「Background Modes」。
  4. 勾选需要的模式:添加成功后,你会看到一堆复选框。嗯,这里要注意,别一股脑全勾上。

核心要点:开启后台模式后,你的App才能在后台继续执行特定任务。否则,系统会在几秒内把你的App挂起。

我个人习惯是,只勾选当前功能真正需要的模式。为什么?因为每个后台模式都会增加电量消耗,审核时也可能被追问。你想想看,一个计算器App勾了「Audio, AirPlay, and Picture in Picture」,审核人员会怎么想?

2.2 Info.plist配置详解

当你勾选了后台模式,Xcode会自动往Info.plist里写入一个叫UIBackgroundModes的键。这个键的值是一个数组,里面装着你勾选的那些模式对应的字符串。

说白了,Xcode的UI操作只是帮你写plist文件。你也可以手动编辑Info.plist,效果完全一样。我有时候为了快速调试,会直接改plist源码。

// Info.plist 中的 UIBackgroundModes 示例
<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
    <string>processing</string>
    <string>audio</string>
</array>

这里有个坑,我曾经遇到过:手动编辑plist时,字符串拼写错了,结果后台模式死活不生效。系统不会报错,只会静默忽略。所以,我建议你尽量用Xcode的UI来操作,减少手误。

避坑指南:Info.plist中的字符串是大小写敏感的。比如「fetch」写成「Fetch」,系统就不认。我曾经因为这个排查了半天,最后发现是首字母大写了。

2.3 常见的后台模式类型

iOS提供了好几种后台模式,每种对应不同的使用场景。我挑几个最常用的给你讲讲。

后台模式 Info.plist字符串 适用场景
Background Fetch fetch 定期从服务器拉取少量数据,比如新闻App刷新头条
Background Processing processing 执行耗时任务,比如数据清洗、图片批量处理
Audio, AirPlay, and Picture in Picture audio 播放音频或视频时保持后台运行
Location Updates location 持续获取位置信息,比如导航App
Voice over IP voip VoIP通话,比如微信语音通话
External Accessory Communication external-accessory 与外部配件通信,比如蓝牙设备
Uses Bluetooth LE Accessories bluetooth-central 与蓝牙低功耗设备通信
Acts as a Bluetooth LE Accessory bluetooth-peripheral 作为蓝牙外设广播数据

这里我想重点说说Background FetchBackground Processing,因为这两个跟咱们的离线下载功能关系最密切。

2.3.1 Background Fetch

这个模式说白了就是让系统在合适的时机唤醒你的App,执行一些轻量级任务。系统会智能地安排唤醒时间,比如用户充电时、连接WiFi时。

我记得有个项目,需要每天早上7点刷新用户订阅的播客列表。用Background Fetch就特别合适。你只需要实现application:performFetchWithCompletionHandler:方法,在里面拉取数据,然后调用completion handler告诉系统你干完了。

- (void)application:(UIApplication *)application 
    performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    
    // 拉取播客列表
    [self fetchPodcastListWithCompletion:^(BOOL success) {
        if (success) {
            completionHandler(UIBackgroundFetchResultNewData);
        } else {
            completionHandler(UIBackgroundFetchResultFailed);
        }
    }];
}

小技巧:Background Fetch的执行时间只有30秒左右。如果任务没做完,系统会终止你的代码。所以,别在这里做重活。

2.3.2 Background Processing

这个模式是iOS 13才引入的,专门用来处理那些需要更长时间的后台任务。系统会在设备空闲、电量充足时执行这些任务。

我建议你用它来做数据迁移、模型训练、批量下载等耗时操作。相比Background Fetch,它的执行窗口更长,但触发频率更低。

// 注册后台处理任务
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.downloadTask", 
                                  using: nil) { task in
    self.handleDownloadTask(task as! BGProcessingTask)
}

func handleDownloadTask(_ task: BGProcessingTask) {
    // 执行下载任务
    task.expirationHandler = {
        // 任务即将超时,保存进度
    }
}

2.3.3 其他模式简述

剩下的模式我就不展开讲了,简单提一下:

  • Audio模式:播放音频时保持后台运行。注意,如果没在播放,系统还是会挂起你。
  • Location Updates:导航类App必备。但要注意电量消耗,我一般会结合significantLocationChange来优化。
  • VoIP:微信、Skype这类App用的。现在苹果推荐用PushKit替代,更省电。

重要提醒:不要滥用后台模式。苹果审核时会对后台模式的使用进行审查。如果你的App没有合理理由使用某个模式,会被拒绝。我曾经有个项目因为勾了「Location Updates」但没实际使用定位功能,被拒了两次。

好了,关于Background Modes Capability就讲这么多。下一章我们会深入讲解URLSession后台下载,那才是实现离线功能的核心。你先把这些基础打好,后面才能跟得上。