Skip to content
Development
Skill

/contacts-framework

Read, create, update, and pick contacts using the Contacts and ContactsUI frameworks. Use when fetching contact data, saving new contacts, wrapping CNContactPickerViewController in SwiftUI, handling contact permissions, or working with CNContactStore fetch and save requests.

From plugin
swift-ios-skills
98186 skills1 MCP
Install
$ npx -y skills add dpearson2699/swift-ios-skills --skill contacts-framework --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/contacts-framework

Context preview

The summary Claude sees to decide when to auto-load this skill.

Read, create, update, and pick contacts using the Contacts and ContactsUI frameworks. Use when fetching contact data, saving new contacts, wrapping CNContactPickerViewController in SwiftUI, handling contact permissions, or working with CNContactStore fetch and save requests.

SKILL.md

contacts-framework.SKILL.md
name: contacts-framework
description: "Read, create, update, and pick contacts using the Contacts and ContactsUI frameworks. Use when fetching contact data, saving new contacts, wrapping CNContactPickerViewController in SwiftUI, handling contact permissions, or working with CNContactStore fetch and save requests."

Contacts Framework

Use `CNContactStore`, `CNSaveRequest`, and `CNContactPickerViewController` to fetch, create, update, or pick contacts in Swift 6.3 / iOS 26+ apps.

Contents

  • [Setup](#setup)
  • [Authorization](#authorization)
  • [Fetching Contacts](#fetching-contacts)
  • [Key Descriptors](#key-descriptors)
  • [Creating and Updating Contacts](#creating-and-updating-contacts)
  • [Contact Picker](#contact-picker)
  • [Observing Changes](#observing-changes)
  • [Common Mistakes](#common-mistakes)
  • [Review Checklist](#review-checklist)
  • [References](#references)

Setup

Project Configuration

1. Add `NSContactsUsageDescription` to Info.plist explaining why the app accesses contacts. The app crashes if it uses contact data APIs without this key. 2. No additional capability or entitlement is required for ordinary Contacts access. 3. Add `com.apple.developer.contacts.notes` only when reading or writing `CNContactNoteKey` / `CNContact.note`; this entitlement requires Apple approval before public distribution.

Imports

@preconcurrency import Contacts  // CNContactStore, CNSaveRequest, CNContact
import ContactsUI                // CNContactPickerViewController

Authorization

Request access before fetching or saving contacts. The picker (`CNContactPickerViewController`) does not require authorization -- the system grants access only to the contacts the user selects.

let store = CNContactStore()

func requestAccess() async throws -> Bool {
    return try await store.requestAccess(for: .contacts)
}

// Check current status without prompting
func checkStatus() -> CNAuthorizationStatus {
    CNContactStore.authorizationStatus(for: .contacts)
}

Authorization States

| Status | Meaning | |---|---| | `.notDetermined` | User has not been prompted yet | | `.authorized` | Full read/write access granted | | `.denied` | User denied access; direct to Settings | | `.restricted` | Parental controls or MDM restrict access | | `.limited` | iOS 18+: user granted access to selected contacts only |

Treat both `.authorized` and `.limited` as usable Contacts API states. With `.limited`, fetch, edit, and delete operations only apply to contacts the user granted or the app created. Use `ContactAccessButton` or `contactAccessPicker(isPresented:completionHandler:)` to let users add contacts to the app's limited-access set.

Fetching Contacts

Use `unifiedContacts(matching:keysToFetch:)` for predicate-based queries. Use `enumerateContacts(with:usingBlock:)` for batch enumeration of all contacts. For large cached address books, first fetch identifiers, then fetch detailed contacts in batches by identifier.

Fetch by Name

func fetchContacts(named name: String) throws -> [CNContact] {
    let predicate = CNContact.predicateForContacts(matchingName: name)
    let keys: [CNKeyDescriptor] = [
        CNContactGivenNameKey as CNKeyDescriptor,
        CNContactFamilyNameKey as CNKeyDescriptor,
        CNContactPhoneNumbersKey as CNKeyDescriptor
    ]
    return try store.unifiedContacts(matching: predicate, keysToFetch: keys)
}

Fetch by Identifier

func fetchContact(identifier: String) throws -> CNContact {
    let keys: [CNKeyDescriptor] = [
        CNContactGivenNameKey as CNKeyDescriptor,
        CNContactFamilyNameKey as CNKeyDescriptor,
        CNContactEmailAddressesKey as CNKeyDescriptor
    ]
    return try store.unifiedContact(withIdentifier: identifier, keysToFetch: keys)
}

Enumerate All Contacts

Perform I/O-heavy enumeration off the main thread.

func fetchAllContacts() throws -> [CNContact] {
    let keys: [CNKeyDescriptor] = [
        CNContactGivenNameKey as CNKeyDescriptor,
        CNContactFamilyNameKey as CNKeyDescriptor
    ]
    let request = CNContactFetchRequest(keysToFetch: keys)
    request.sortOrder = .givenName

    var contacts: [CNContact] = []
    try store.enumerateContacts(with: request) { contact, _ in
        contacts.append(contact)
    }
    return contacts
}

Key Descriptors

Only fetch the properties you need. Accessing an unfetched property throws `CNContactPropertyNotFetchedException`.

Common Keys

| Key | Property | |---|---| | `CNContactGivenNameKey` | First name | | `CNContactFamilyNameKey` | Last name | | `CNContactPhoneNumbersKey` | Phone numbers array | | `CNContactEmailAddressesKey` | Email addresses array | | `CNContactPostalAddressesKey` | Mailing addresses array | | `CNContactImageDataKey` | Full-resolution contact photo | | `CNContactThumbnailImageDataKey` | Thumbnail contact photo | | `CNContactBirthdayKey` | Birthday date components | | `CNContactOrganizationNameKey` | Company name |

Composite Key Descriptors

Use `CNContactFormatter.descriptorForRequiredKeys(for:)` to fetch all keys needed for formatting a contact's name.

let nameKeys = CNContactFormatter.descriptorForRequiredKeys(for: .fullName)
let keys: [CNKeyDescriptor] = [nameKeys, CNContactPhoneNumbersKey as CNKeyDescriptor]

Creating and Updating Contacts

Use `CNMutableContact` to build new contacts and `CNSaveRequest` to persist changes.

Creating a New Contact

func createContact(givenName: String, familyName: String, phone: String) throws {
    let contact = CNMutableContact()
    contact.givenName = givenName
    contact.familyName = familyName
    contact.phoneNumbers = [
        CNLabeledValue(
            label: CNLabelPhoneNumberMobile,
            value: CNPhoneNumber(stringValue: phone)
        )
    ]

    let saveRequest = CNSaveRequest()
    saveRequest.add(contact, toContainerWithIdentifier: nil) // nil = default container
    try store.execu
Read more
Ships withswift-ios-skills

86 agent skills optimized for iOS 26+ development with Swift 6.3 and modern Apple frameworks.

Get the whole plugin
Stats
981
Stars
50
Forks
Active
Maintenance
Python
Language
9d ago
Last commit
5mo ago
Created

Repo: dpearson2699/swift-ios-skills

Other skills on swift-ios-skills.