uni.chooseContact是最稳的选人方式,仅返回用户手动选择的一个联系人;全量读取需用plus.contacts,但iOS与Android实现差异大,且需注意权限配置、字段兼容性及搜索需自行实现。

uni.chooseContact 是最稳的“选人”方式,但不是“读全量通讯录”
想让用户点一下从系统通讯录里挑一个联系人?uni.chooseContact 是官方推荐、跨平台兼容性最好的方案。它不申请 READ_CONTACTS 权限,也不需要处理 iOS 的 CNContactStore 或 Android 的 ContactsContract,直接唤起原生选择器,返回姓名和电话(部分平台还带邮箱、头像)。
但它有明确边界:只返回用户手动点选的那一个联系人,无法拿到全部列表、不能搜索、不能分组、不能过滤。如果你要的是“通讯录通讯录管理”或“联系人搜索框”,这条路走不通。
- 适用场景:邀请好友、快速填写收货人、一键拨号前确认联系人
- 失败常见原因:
scope.contact未授权、iOS 未在manifest.json中配置NSContactsUsageDescription、Android 12+ 未声明READ_CONTACTS权限(即使不用全量读取,部分厂商仍会校验) - 调用后返回对象结构简单:
{ name: '张三', mobile: '138****1234' },注意mobile字段在 iOS 和某些安卓定制系统中可能为空,需 fallback 到value或遍历phoneNumbers
全量读取必须用 plus.contacts,但 iOS 和 Android 写法完全不同
要真正“获取手机通讯录全部联系人并支持搜索”,必须绕过 uni.* 封装,直连 plus.contacts API。这不是 bug,是设计使然——uni-app 把跨平台抽象留给了上层业务,底层能力则暴露给原生扩展。
iOS 和 Android 在字段结构、权限粒度、甚至联系人来源(手机/SIM/账户)上差异极大,硬写成一套逻辑极易崩溃。
- iOS 必须先调
plus.ios.requestAuthorization,传plus.ios.AUTHORIZATION_CONTACTS;Android 则用plus.android.requestPermissions(['android.permission.READ_CONTACTS']) - 字段获取不能写死:
displayName在 iOS 是字符串,在 Android 可能是数组;phoneNumbers在 iOS 是CNPhoneNumber对象,Android 返回的是字符串数组,需用.value或正则清洗空格 - 性能敏感:全量读取几百个联系人时,iOS 的
enumerateContactsWithFetchRequestErrorUsingBlock是同步阻塞的,建议加 loading + 节流,避免白屏卡顿
manifest.json 权限配置漏一项,真机就静默失败
很多开发者在 HBuilderX 里勾选了“通讯录”权限,却忘了看生成的 manifest.json 底层配置,结果打包后 iOS 提示“无权限”,Android 直接回调 fail 且不报错——因为权限没真正注入到原生包里。
- iOS 必须在
"app-plus".distribute.ios.privacyDescription下写"NSContactsUsageDescription",值不能为空字符串,否则 App Store 审核被拒 - Android 不只要
"android.permission.READ_CONTACTS",还得加上"android.permission.GET_ACCOUNTS"(尤其对华为、小米等机型,否则plus.contacts.getAddressBook返回 undefined) - 别信“H5 模式也能读通讯录”:浏览器受同源策略和安全限制,
plus对象在非 app 环境下为 undefined,必须走 app 端调试
搜索功能得自己实现,别指望原生接口支持关键词过滤
plus.contacts.find 或 findAll 只支持按字段名预取(如 ['displayName', 'phoneNumbers']),不接受 SQL 式 where 条件。所谓“搜索”,本质是把全量联系人 load 进内存,再用 JS 做模糊匹配。
这带来两个现实问题:一是首次加载慢(尤其联系人超 500 条),二是中文搜索易出错(拼音首字母、多音字、空格干扰)。
- 建议预处理:在
onLoad里一次性读取并缓存到this.contacts,后续搜索直接 filter,避免重复 IO - 搜索逻辑别只用
includes:对displayName做pinyin.get(…).slice(0, keyword.length)匹配首字母,再 fallback 到全文 indexOf - SIM 卡联系人默认不包含在
ADDRESSBOOK_PHONE中,要读 SIM 需显式调plus.contacts.ADDRESSBOOK_SIM,且 Android 多数厂商已禁用该接口,iOS 根本不支持——别为这个需求投入开发时间
uni.chooseContact + 后端模糊查询补全。真要本地全量+搜索,就得接受 iOS/Android 两套逻辑、权限反复调试、以及用户对首次加载延迟的容忍。这些不是框架缺陷,而是移动操作系统对隐私的刚性约束。

















