PythonIDE Docs
中文
简体中文

contacts

联系人读取、编辑、选择器和 vCard 导入导出。

访问系统通讯录:权限、读取、搜索、编辑、分组、系统选择器与 vCard 导入导出。

边界:联系人属于敏感数据。默认只读当前功能需要的字段和数量;列表页用 limit/offset;修改后需 save() 提交。token 等凭据不要用通讯录存,请用 keychain

#模块概览

说明
导入import contacts
适合做什么选人、搜索联系人、展示卡片、创建/编辑联系人
调用时机读取和选择器放在按钮回调;不要首屏批量读取
推荐顺序is_authorizedrequest_access → 读取/选择 → 修改后 save()
编辑事务add_person 等改动需 save() 落盘;失败时 revert()

#快速开始

下面脚本申请权限,读取前 10 个联系人并搜索名字:

python
import contacts

if not contacts.is_authorized():
    contacts.request_access()

if not contacts.is_authorized():
    print("通讯录未授权")
else:
    people = contacts.get_all_people(limit=10)
    for person in people:
        name = getattr(person, "full_name", "") or "未命名"
        print(person.identifier, name)

    matches = contacts.find("张")
    print("搜索到", len(matches), "个匹配")

#AppUI 示例

读取和系统选择器都放在按钮回调;列表不加载头像和 vCard。

python
import appui
import contacts

state = appui.State(
    status="等待操作",
    selected="—",
    people=[],
)


def person_key(person):
    return person["id"]


def person_row(person):
    return appui.VStack([
        appui.Text(person["name"]),
        appui.Text(person["phone"]).font("caption").foreground_color("secondaryLabel"),
    ], spacing=2)


def load_people():
    if not contacts.is_authorized():
        contacts.request_access()
    if not contacts.is_authorized():
        state.batch_update(status="未授权", people=[], selected="—")
        return

    rows = []
    for person in contacts.get_all_people(limit=20):
        phones = getattr(person, "phone_numbers", []) or []
        phone = phones[0] if phones else "—"
        rows.append({
            "id": person.identifier or str(person.id),
            "name": (
                getattr(person, "full_name", "")
                or getattr(person, "organization", "")
                or "未命名联系人"
            ),
            "phone": str(phone),
        })
    state.batch_update(
        status=f"已读取 {len(rows)} 个联系人",
        people=rows,
    )


def pick_one():
    if not contacts.is_authorized():
        contacts.request_access()
    if not contacts.is_authorized():
        state.status = "未授权,无法打开选择器"
        return

    picked = contacts.pick_contact()
    if not picked:
        state.status = "用户取消选择"
        return
    name = getattr(picked, "full_name", "") or "未命名联系人"
    state.batch_update(
        selected=name,
        status="已通过系统选择器选中联系人",
    )


def body():
    return appui.NavigationStack(
        appui.List([
            appui.Section("操作", [
                appui.Button("读取联系人", action=load_people)
                .button_style("bordered_prominent"),
                appui.Button("系统选择器选人", action=pick_one),
                appui.LabeledContent("状态", value=state.status),
                appui.LabeledContent("已选", value=state.selected),
            ]),
            appui.Section("列表", [
                appui.ForEach(state.people, row_builder=person_row, key=person_key),
            ], footer="默认 limit=20,不含头像与 vCard。"),
        ]).navigation_title("通讯录")
    )


appui.run(body, state=state)

#API 参考

#速查

API作用
is_authorized() / request_access()权限判断与申请
get_all_people(limit, offset)分页读取联系人
get_person(id, ...)读取单个联系人详情
find(name)按名字搜索
pick_contact()系统选择器选一人
add_person / save() / revert()编辑事务
export_vcards / import_vcardsvCard 导入导出

#权限

API说明
authorization_status()查询授权状态
is_authorized()是否可访问通讯录
request_access()申请权限
manage_limited_access()调整「有限访问」联系人
capabilities()当前设备/系统能力
python
if not contacts.is_authorized():
    contacts.request_access()

#读取与搜索

get_all_people(limit=100, offset=0) — 分页读取,列表页务必设 limit

get_person(person_or_id, include_image_data=False, include_vcard=False) — 读取详情;头像和 vCard 按需开启。

python
people = contacts.get_all_people(limit=20, offset=0)
person = contacts.get_person(people[0], include_image_data=False)
matches = contacts.find("Ada")
by_phone = contacts.find_by_phone("138")
by_email = contacts.find_by_email("ada@example.com")

其他:get_me() 读取「我的名片」。

#编辑事务

修改不是立即落盘,需 save() 提交;失败时 revert() 回滚。

python
person = contacts.new_person(seed={"given_name": "Ada", "family_name": "Lovelace"})
contacts.add_person(person)
try:
    contacts.save()
except Exception:
    contacts.revert()
    raise
API说明
new_person(seed=...)创建新联系人对象
add_person(person)加入待提交队列
remove_person(person)标记删除
edit_person(person, **kwargs)修改字段
save()提交到系统通讯录
revert()放弃待提交修改

分组与容器:get_all_groupsadd_groupget_people_in_groupget_all_containers 等。

#系统选择器

由用户主动选择,比无提示批量读取更稳妥:

python
picked = contacts.pick_contact()
if picked:
    contacts.show_person(picked, allows_editing=False, allows_actions=True)

multi = contacts.pick_contacts()
prop = contacts.pick_property(kind="phone")

pick_contact() 返回 None 表示用户取消。

#vCard 与变更

python
data = contacts.export_vcards([person])
contacts.import_vcards(data)
history = contacts.get_change_history(token=None)

#常见错误

错误写法后果修正
首屏读取全部联系人隐私体验差、可能很慢按钮触发 + limit
修改后忘记 save()变更未落盘成功路径调用 save()
保存失败继续用待提交对象状态不一致revert() 后重试
默认 include_image_data=True内存和隐私成本高只在头像页按需加载

#相关文档

文档用途
permission统一权限查询(Bridge 对 contacts 支持有限)
keychain保存 token,不用通讯录
原生能力入口MiniApp 场景配方

#预期效果

运行示例后,界面应出现文档描述的目标结果;若与预期不符,先看「失败路径」并按返回值或日志排查。