
Contacts Framework
Wire your iOS app to read, create, update, and pick contacts with CNContactStore and SwiftUI pickers while handling permissions correctly.
Overview
contacts-framework is an agent skill for the Build phase that reads, creates, updates, and picks iOS contacts using CNContactStore, CNSaveRequest, and ContactsUI pickers.
Install
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill contacts-frameworkWhat is this skill?
- Covers authorization, CNContactStore fetch patterns, and CNSaveRequest create/update flows for Swift 6.3 / iOS 26+
- Documents key descriptors, change observation, and CNContactPickerViewController wrapped for SwiftUI
- Lists Info.plist NSContactsUsageDescription and optional notes entitlement setup
- Includes common mistakes section and a review checklist before shipping contact features
- Picker path documented as not requiring prior authorization like full store access
- Targets Swift 6.3 / iOS 26+
- Includes a review checklist and common-mistakes section
Adoption & trust: 1.6k installs on skills.sh; 713 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need Contacts data or a native picker in SwiftUI but are unsure about permissions, descriptors, save requests, and Apple’s privacy rules.
Who is it for?
Solo builders shipping SwiftUI or UIKit apps that must sync, display, or let users select from the on-device contact database.
Skip if: Backend-only APIs, Android/React Native contact modules, or apps that only need share-sheet contact cards without CNContactStore.
When should I use this skill?
Fetching contact data, saving new contacts, wrapping CNContactPickerViewController in SwiftUI, handling contact permissions, or working with CNContactStore fetch and save requests.
What do I get? / Deliverables
Your agent implements authorized fetch/save and picker flows with correct plist keys, entitlements when needed, and a review checklist before merge.
- Contacts authorization flow
- Fetch/save/picker implementation
- Review-checklist-passing contact feature code
Recommended Skills
Journey fit
Contact database access is implemented during product build when you integrate Apple system frameworks into the app. Contacts is a platform API integration—fetch/save requests, entitlements, and ContactsUI bridges—not standalone UI polish.
How it compares
Use for native Apple Contacts APIs—not a third-party CRM SDK or server-side contact storage skill.
Common Questions / FAQ
Who is contacts-framework for?
Indie iOS developers and agent-assisted Xcode projects that need compliant Contacts and ContactsUI integration on recent Swift and iOS versions.
When should I use contacts-framework?
During Build when fetching contact fields, saving new contacts, handling CNContactStore authorization, wrapping CNContactPickerViewController in SwiftUI, or debugging descriptor and save-request mistakes.
Is contacts-framework safe to install?
Review the Security Audits panel on this Prism page and inspect the skill source in your repo before letting an agent modify entitlements or privacy strings.
SKILL.md
READMESKILL.md - Contacts Framework
# Contacts Framework Fetch, create, update, and pick contacts from the user's Contacts database using `CNContactStore`, `CNSaveRequest`, and `CNContactPickerViewController`. Targets Swift 6.3 / iOS 26+. ## 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 2. No additional capability or entitlement is required for basic Contacts access 3. For contact notes access, add the `com.apple.developer.contacts.notes` entitlement ### Imports ```swift 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. ```swift 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 | ## Fetching Contacts Use `unifiedContacts(matching:keysToFetch:)` for predicate-based queries. Use `enumerateContacts(with:usingBlock:)` for batch enumeration of all contacts. ### Fetch by Name ```swift 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 ```swift 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. ```swift 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 | | `CNConta