Swift命令行工具操作通讯录遇NSCocoaErrorDomain Code=134092及权限问题
问题背景
使用Swift Package Manager开发命令行程序,通过Contacts API自动为联系人添加拼音姓名,但带备注的联系人无法完成更新,出现两类错误:
1. 枚举联系人时的权限错误
枚举过程中输出以下权限相关日志:
2023-01-22 18:58:36.085525+0800 PhoneticNames[18591:98356] [core] Attempted to register account monitor for types client is not authorized to access: {("com.apple.account.CardDAV","com.apple.account.Exchange","com.apple.account.LDAP")} 2023-01-22 18:58:36.085580+0800 PhoneticNames[18591:98356] [accounts] CNAccountCollectionUpdateWatcher 0x6000003ccc40: Store registration failed: Error Domain=com.apple.accounts Code=7 "(null)" 2023-01-22 18:58:36.085606+0800 PhoneticNames[18591:98356] [accounts] CNAccountCollectionUpdateWatcher 0x6000003ccc40: Update event received, but store registration failed. This event will be handled, but the behavior is undefined. 2023-01-22 18:58:36.136236+0800 PhoneticNames[18591:98344] [api] Attempt to read notes by an unentitled app
根据苹果官方文档,仅当请求CNContactNoteKey时才需添加com.apple.developer.contacts.notes权限,但未请求该字段仍触发错误。枚举代码如下:
let keys: [CNKeyDescriptor] = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactFormatter.descriptorForRequiredKeys(for: .phoneticFullName)] let fetchRequest = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor]) fetchRequest.mutableObjects = true do { try store.enumerateContacts(with: fetchRequest) { (contact, stop) in contacts.append(contact.mutableCopy() as! CNMutableContact) } } catch { print("unable to list contacts. \(error)") }
2. 带备注联系人更新失败
代码对无备注联系人正常工作,但带非空备注的联系人执行saveRequest时失败,输出:
2023-01-22 18:58:36.197026+0800 PhoneticNames[18591:98344] [error] error: Unhandled error occurred during faulting: Error Domain=NSCocoaErrorDomain Code=134092 "(null)" ({}) CoreData: error: Unhandled error occurred during faulting: Error Domain=NSCocoaErrorDomain Code=134092 "(null)" ({})
更新代码如下:
let saveRequest = CNSaveRequest() contact.phoneticFamilyName = phoneticFamilyName contact.phoneticMiddleName = phoneticMiddleName contact.phoneticGivenName = phoneticGivenName saveRequest.update(contact) do { try store.execute(saveRequest) } catch { print("Fail to execute store request: \(error)") }
开发环境:Swift 5.7、Xcode 14.2、macOS 13.1
错误成因分析
权限警告的根源
- 隐式字段请求:
CNContactFormatter.descriptorForRequiredKeys(for:)返回的描述符可能包含超出预期的字段(文档未明确说明),部分场景下会隐式包含CNContactNoteKey,触发权限检查。 - Mutable联系人加载逻辑:设置
fetchRequest.mutableObjects = true时,系统会尝试加载完整的联系人对象以支持修改操作,即使未显式请求备注字段,带备注的联系人仍会触发备注字段的权限校验。 - 账户类型限制:日志中提到的CardDAV/Exchange/LDAP账户类型,其联系人数据访问规则更严格,即使未请求敏感字段,也可能触发额外的权限检查逻辑。
更新失败(Code=134092)的原因
NSCocoaErrorDomain Code=134092本质是权限不足导致的CoreData故障:当尝试更新带备注的联系人时,系统需要处理完整的联系人实体(包括未修改的备注字段)。由于程序没有com.apple.developer.contacts.notes权限,系统加载备注字段时触发错误,进而引发CoreData层面的故障,最终导致更新请求失败。
解决方案
方案1:明确指定需要的字段,避免隐式请求
替换CNContactFormatter的描述符,直接指定仅需的字段,避免隐式请求备注等敏感字段:
let keys: [CNKeyDescriptor] = [ CNContactFamilyNameKey as CNKeyDescriptor, CNContactGivenNameKey as CNKeyDescriptor, CNContactMiddleNameKey as CNKeyDescriptor, CNContactPhoneticFamilyNameKey as CNKeyDescriptor, CNContactPhoneticGivenNameKey as CNKeyDescriptor, CNContactPhoneticMiddleNameKey as CNKeyDescriptor ] let fetchRequest = CNContactFetchRequest(keysToFetch: keys) fetchRequest.mutableObjects = true
方案2:添加备注访问权限
如果方案1无法解决问题,需在程序的entitlements文件中添加com.apple.developer.contacts.notes权限:
<key>com.apple.developer.contacts.notes</key> <true/>
命令行程序需正确配置entitlements,可在Xcode中通过目标的"Signing & Capabilities"添加Contacts权限并勾选"Notes"选项,或手动编辑entitlements文件。
内容的提问来源于stack exchange,提问作者jtchen2k

