迁移到新的 expo-contacts API

编辑页面

从旧版 expo-contacts API 迁移到使用 Contact 的新版基于类的 expo-contacts API。


For the complete documentation index, see llms.txt. Use this file to discover all available pages.

新的基于类的 expo-contacts API 现已稳定。旧版 API 可从 expo-contacts/legacy 获取。请迁移到根目录的 expo-contacts 导入,以便使用新 API 并享受未来的修复。

新 API 用 Contact 类替代了基于函数的 API。联系人被表示为类实例,实例中只保存本地联系人 ID。主要变化如下:

  • 联系人属性(姓名、公司、生日等)现在是异步 getter 和 setter,而不再是普通对象属性。
  • 子记录(电话、邮箱、地址等)通过专门的 add*/get*/update*/delete* 方法管理,而不是重写整个数组。
  • 现在提供两种更新方法:用于局部更新的 patch,以及用于完整替换的 update

安装

安装与 SDK 兼容的包:

Terminal
npx expo install expo-contacts

导入新 API

expo-contacts 导入:

import { Contact } from 'expo-contacts';

联系人

创建联系人

// 之前 const id = await Contacts.addContactAsync({ firstName: 'John', lastName: 'Doe' }); // 之后 const contact = await Contact.create({ givenName: 'John', familyName: 'Doe' });

Contact.create 返回的是 Contact 实例,而不只是 ID。

获取所有联系人

// 之前 const { data } = await Contacts.getContactsAsync({ fields: [Contacts.Fields.Name, Contacts.Fields.PhoneNumbers], pageSize: 20, pageOffset: 10, sort: Contacts.SortTypes.FirstName, }); // 之后,实例对象 const contacts = await Contact.getAll({ limit: 20, offset: 10, sortOrder: ContactsSortOrder.GivenName, }); // 之后,类型化字段投影 const contacts = await Contact.getAllDetails([ContactField.FULL_NAME, ContactField.PHONES], { limit: 20, offset: 10, });

getAllDetails 返回的是强类型投影,结果会被缩窄为请求的字段。

按 ID 获取联系人

getAllDetails 的结果中包含联系人 ID。如果你想对 getAllDetails 返回的联系人调用方法,可以使用构造函数将其包装为 Contact 实例:

const results = await Contact.getAllDetails([ContactField.FULL_NAME, ContactField.PHONES]); const contact = new Contact(results[0].id); await contact.addPhone({ label: 'work', number: '+12345678912' });

统计联系人数量

// 之前 const hasAny = await Contacts.hasContactsAsync(); // 之后 const hasAny = await Contact.hasAny(); // 之后,之前没有直接对应方法 const count = await Contact.getCount();

更新联系人

// 之前,重写整个联系人 await Contacts.updateContactAsync({ ...contact, firstName: 'Andrew' }); // 之后,局部更新(仅修改提供的字段) await contact.patch({ givenName: 'Andrew' }); // 之后,完整替换 - 未提供的所有字段都将被清空 await contact.update({ givenName: 'John', familyName: 'Doe', phones: [{ label: 'mobile', number: '+12123456789' }], });

删除联系人

// 之前 await Contacts.removeContactAsync(id); // 之后 await contact.delete();

标量字段

所有标量联系人属性现在都是异步 getter 和 setter。使用 get* 读取,使用 set* 写入。你也可以通过 contact.getDetails() 一次获取多个字段。

字段GetterSetter
getGivenNamesetGivenName
getFamilyNamesetFamilyName
中间名getMiddleNamesetMiddleName
全名getFullName
昵称(仅 iOS)getNicknamesetNickname
前缀getPrefixsetPrefix
后缀getSuffixsetSuffix
名的拼音getPhoneticGivenNamesetPhoneticGivenName
姓的拼音getPhoneticFamilyNamesetPhoneticFamilyName
公司getCompanysetCompany
职位getJobTitlesetJobTitle
部门getDepartmentsetDepartment
生日(仅 iOS)getBirthdaysetBirthday
备注getNotesetNote
图片getImagesetImage
缩略图getThumbnail
收藏(仅 Android)getIsFavouritesetIsFavourite

姓名

// 之前,联系人通过 getContactByIdAsync 获取 const contact = await Contacts.getContactByIdAsync(id); console.log(contact.firstName, contact.lastName); // 之后,分别使用异步 getter const givenName = await contact.getGivenName(); const familyName = await contact.getFamilyName(); // 之后,通过 getDetails() 获取完整姓名对象 const details = await contact.getDetails([ContactField.FULL_NAME]); await contact.setGivenName('John'); await contact.setFamilyName('Doe'); await contact.setMiddleName('Michael');

子记录

子记录不再通过 updateContactAsync 重写整个数组来管理。每种类型都有专门的 add*get*update*delete* 方法:

子记录方法
电话号码addPhone, getPhones, updatePhone, deletePhone
邮箱addEmail, getEmails, updateEmail, deleteEmail
地址addAddress, getAddresses, updateAddress, deleteAddress
URLaddUrlAddress, getUrlAddresses, updateUrlAddress, deleteUrlAddress
社交资料addSocialProfile, getSocialProfiles, updateSocialProfile, deleteSocialProfile
IM 地址addImAddress, getImAddresses, updateImAddress, deleteImAddress
日期addDate, getDates, updateDate, deleteDate
额外姓名(仅 Android)addExtraName, getExtraNames, updateExtraName, deleteExtraName

下面的示例使用电话号码。

// 之前,重写整个数组 await Contacts.updateContactAsync({ ...contact, phoneNumbers: [...existing, { label: 'work', number: '+12345678912' }], }); // 之后,添加 await contact.addPhone({ label: 'work', number: '+12345678912' }); // 之后,获取 const phones = await contact.getPhones(); // 之后,更新 await contact.updatePhone(existingPhone); // 之后,删除 await contact.deletePhone(existingPhone);

原生 UI

// 之前 const contact = await Contacts.presentContactPickerAsync(); await Contacts.presentFormAsync(null, contactData, { isNew: true }); await Contacts.presentFormAsync(contactId); // 之后 const contact = await Contact.presentPicker(); if (contact) { // 用户选择了一个联系人 } const created = await Contact.presentCreateForm(contactData); await contact.editWithForm();

访问选择器(仅 iOS 18+)

// 之前 const contactIds = await Contacts.presentAccessPickerAsync(); // 之后 const selectedContacts = await Contact.presentAccessPicker();

分组(仅 iOS)

// 之前 const groups = await Contacts.getGroupsAsync({}); await Contacts.createGroupAsync('Family'); await Contacts.addExistingContactToGroupAsync(contactId, groupId); await Contacts.removeContactFromGroupAsync(contactId, groupId); // 之后 const groups = await Group.getAll(); const group = await Group.create('Family'); await group.addContact(contact); await group.removeContact(contact); const contacts = await group.getContacts(); const name = await group.getName(); await group.setName('Close Friends'); await group.delete();

容器(仅 iOS)

// 之前 const containers = await Contacts.getContainersAsync({}); const defaultId = await Contacts.getDefaultContainerIdAsync(); // 之后 const containers = await Container.getAll(); const defaultContainer = await Container.getDefault(); // 可能为 null const name = await container.getName(); const type = await container.getType(); const groups = await container.getGroups(); const contacts = await container.getContacts();

权限

// 之前 const { status } = await Contacts.requestPermissionsAsync(); const { status } = await Contacts.getPermissionsAsync(); // 之后 const { status } = await requestPermissionsAsync(); const { status } = await getPermissionsAsync();

监听变化

// 之前 const subscription = Contacts.addContactsChangeListener(() => { // 联系人已更改 }); subscription.remove(); // 之后 const subscription = addContactsChangeListener(() => { // 联系人已更改 }); subscription.remove(); // 一次移除所有监听器 removeAllContactsChangeListeners();

破坏性语义变化

  • 字段名遵循平台约定。例如,firstName/lastName 变为 givenName/familyName
  • 字段选择使用类型化的 ContactField 枚举。getAllDetails 的结果类型会缩窄为请求的字段。
  • 去掉了 Async 后缀。整个库都是异步的。
  • 联系人属性现在是异步 getter 和 setter,而不是联系人对象上的普通属性。使用 contact.getDetails() 一次获取多个字段。
  • 子记录(电话、邮箱、地址等)通过专门的 add*/get*/update*/delete* 方法管理,而不是通过 updateContactAsync 重写整个数组。
  • 两种更新方法取代了 updateContactAsyncpatch 应用局部更改,update 替换整个联系人。
  • shareContactAsyncwriteContactToFileAsync 已移除,且没有替代方案。你可以安全地删除对这些函数的任何调用。

参考

联系人

查看 expo-contacts 的完整 API 参考。