Swift iOS:为现有通讯录联系人添加更新地址报错解决
需求场景
在UITableViewCell中展示联系人列表,点击已有联系人对应的单元格时,为该联系人新增或更新地址信息。
相关定义
var selectedContact: Contact!
说明:
Contact!为自定义数据模型,非系统Contacts框架提供的CNContact类型
var updateContact = CNContact()
现有实现与报错
现有联系人地址更新代码如下:
func AddMultipleLocations(){ var contact = CNMutableContact() contact = selectedAddress.mutableCopy() as! CNMutableContact // 编译报错:Value of type Contact has no member mutableCopy contact.postalAddresses.append(CNLabeledValue(label: CNLabelHome, value: homeAddress)) print(homeAddress) do { let saveRequest = CNSaveRequest() saveRequest.update(contact) let contactstore = CNContactStore() try contactstore.execute(saveRequest) self.showToast(message: "Contact Updated ", font: .systemFont(ofSize: 12.0)) } catch let error { print(error.localizedDescription) self.showToast(message: error.localizedDescription, font: .systemFont(ofSize: 12.0)) } }
编译阶段直接抛出错误:Value of type Contact has no member mutableCopy,即Contact类型无mutableCopy成员,无法完成联系人地址更新逻辑。
错误原因
- 核心问题是类型不匹配:
mutableCopy()是系统CNContact类的方法,用来将不可变的CNContact转为可编辑的CNMutableContact实例;而代码中调用mutableCopy()的对象是自定义的Contact模型,该类型没有实现这个方法,自然触发编译错误 - 代码存在笔误:声明的选中联系人为
selectedContact,实际调用时错写为selectedAddress - 逻辑漏洞:自定义模型不能直接强转成系统通讯录的
CNMutableContact类型,二者没有继承关系,必须通过系统通讯录提供的接口拉取对应联系人记录才能修改。
修复方案
- 自定义
Contact模型必须持久化存储对应系统联系人的identifier字段(这是关联自定义数据和系统通讯录联系人的唯一标识,拉取、修改系统联系人都依赖这个id) - 拿到选中联系人的id后,通过
CNContactStore拉取最新的系统联系人记录,再转为可编辑的CNMutableContact - 校验通讯录权限后,追加地址信息并执行保存操作
修复后的可运行代码:
// 方法名按Swift编码规范改为小驼峰 func addMultipleLocations() { let contactStore = CNContactStore() // 校验自定义联系人是否关联了有效系统联系人id,假设自定义Contact的系统联系人id字段名为systemContactId,根据自己的模型字段名调整即可 guard let contactId = selectedContact.systemContactId else { showToast(message: "联系人信息无效", font: .systemFont(ofSize: 12.0)) return } do { // 拉取系统联系人,必须指定要访问的字段键 let fetchKeys = [CNContactPostalAddressesKey] as [CNKeyDescriptor] let originContact = try contactStore.unifiedContact(withIdentifier: contactId, keysToFetch: fetchKeys) // 转为可编辑的可变联系人对象 guard let mutableContact = originContact.mutableCopy() as? CNMutableContact else { showToast(message: "获取可编辑联系人失败", font: .systemFont(ofSize: 12.0)) return } // 追加新的家庭地址 mutableContact.postalAddresses.append(CNLabeledValue(label: CNLabelHome, value: homeAddress)) // 执行保存 let saveRequest = CNSaveRequest() saveRequest.update(mutableContact) try contactStore.execute(saveRequest) showToast(message: "联系人地址更新成功", font: .systemFont(ofSize: 12.0)) } catch { print(error.localizedDescription) showToast(message: error.localizedDescription, font: .systemFont(ofSize: 12.0)) } }
注意事项
- 操作通讯录前必须先获取用户授权,首次启动时调用
contactStore.requestAccess(for: .contacts)弹出权限申请框,未授权状态下所有通讯录接口都会调用失败 - 拉取系统联系人时必须把要读写的字段加到
keysToFetch参数里,否则访问对应字段会触发崩溃 - 不要尝试直接把自定义模型强转成系统
CNContact/CNMutableContact类型,二者没有继承关系,强制转换会直接崩溃
内容的提问来源于stack exchange,提问作者Muhammad Mustafeez Ur Rehman
相关产品推荐
相关产品推荐

