iPhone锁定状态下UNNotificationSound自定义通知音无法播放
锁定状态下通知自定义音效失效问题原因与修复方案
核心诱因
问题本质是App Group容器内文件的默认数据保护权限和Notification Service Extension的运行环境权限不匹配:
- 你在主应用中向App Group容器的
Library/Sounds目录写入音效文件时,没有显式指定文件保护级别,目录和文件默认继承容器的NSFileProtectionComplete保护属性。该属性下,设备锁定后(系统默认有10秒左右的保护缓冲期,之后加密生效),所有受保护的文件会被系统加密,即便是有权限访问App Group的Extension进程,也无法读取文件内容。 - 当系统在Extension中尝试加载你指定的自定义音效文件时,因为文件被加密无法读取,会直接触发通知音效加载失败,此时系统不会回退到默认通知音,也不会触发振动,和你观察到的现象完全一致。
- 放在主Bundle中的音效之所以能正常播放,是因为主Bundle内的静态资源默认使用
NSFileProtectionCompleteUntilFirstUserAuthentication保护级别,只要用户首次解锁过设备,后续哪怕再次锁定,进程也可以正常读取这些文件,不会被加密拦截。
修复步骤
- 在创建
Library/Sounds目录、完成音效文件拷贝后,显式将目录和所有音效文件的保护级别修改为NSFileProtectionCompleteUntilFirstUserAuthentication,参考代码如下:
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.xxx.xxx")! let soundsURL = containerURL.appendingPathComponent("Library/Sounds/", isDirectory: true) if !FileManager.default.fileExists(atPath: soundsURL.path) { try! FileManager.default.createDirectory(atPath: soundsURL.path, withIntermediateDirectories: true) // 给新建的目录设置正确的保护属性 try? FileManager.default.setAttributes([ .protectionKey: FileProtectionType.completeUntilFirstUserAuthentication ], ofItemAtPath: soundsURL.path) } // 拷贝音效文件 let targetSoundURL = soundsURL.appendingPathComponent(sourceURL.lastPathComponent) try? FileManager.default.copyItem(at: sourceURL, to: targetSoundURL) // 给拷贝完成的音效文件设置相同的保护属性 try? FileManager.default.setAttributes([ .protectionKey: FileProtectionType.completeUntilFirstUserAuthentication ], ofItemAtPath: targetSoundURL.path)
- 对之前已经写入到
Library/Sounds目录的存量音效文件,遍历目录下所有文件,批量补设上述保护属性,避免老文件依然受默认权限限制。
注意事项
- 不建议将文件保护级别设置为
NSFileProtectionNone,会不必要地降低用户数据安全性,completeUntilFirstUserAuthentication是苹果官方推荐的、可供Extension在锁定状态访问的权限级别,兼顾安全性和可访问性。 - 你的音效文件在解锁状态下可以正常播放,说明文件格式、时长(要求≤30秒)、路径配置均符合要求,不需要额外调整这部分逻辑。
内容的提问来源于stack exchange,提问作者user4779109
相关产品推荐
相关产品推荐

