accessorysetupkit
Discover and configure Bluetooth and Wi-Fi accessories using AccessorySetupKit. Use when presenting a privacy-preserving accessory picker, defining discovery…
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.
$ npx -y skills add dpearson2699/swift-ios-skills --skill contacts-framework --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/contacts-frameworkContext 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.
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."
Use `CNContactStore`, `CNSaveRequest`, and `CNContactPickerViewController` to fetch, create, update, or pick contacts in Swift 6.3 / iOS 26+ apps.
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.
@preconcurrency import Contacts // CNContactStore, CNSaveRequest, CNContact import ContactsUI // CNContactPickerViewController
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)
}| 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.
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.
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)
}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)
}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
}Only fetch the properties you need. Accessing an unfetched property throws `CNContactPropertyNotFetchedException`.
| 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 |
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]
Use `CNMutableContact` to build new contacts and `CNSaveRequest` to persist changes.
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.execu86 agent skills optimized for iOS 26+ development with Swift 6.3 and modern Apple frameworks.
Repo: dpearson2699/swift-ios-skills
Discover and configure Bluetooth and Wi-Fi accessories using AccessorySetupKit. Use when presenting a privacy-preserving accessory picker, defining discovery…
Implement, review, or improve Live Activities and Dynamic Island experiences in iOS apps using ActivityKit. Use when building real-time updating widgets for…
Measure ad effectiveness with privacy-preserving attribution using AdAttributionKit. Use when registering ad impressions, handling attribution postbacks,…
Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple Watch system UI. Covers…
Build iOS App Clips with invocation URLs, App Clip Codes, NFC, QR codes, Safari banners, Maps, Messages, target setup, App Store Connect experiences,…
Implement App Intents for Siri, Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence on iOS. Covers AppIntent actions, AppEntity and…