
Getting started with the Contacts framework
Learn how to get access to the user contacts for your SwiftUI applications.
If you need to access or find user contacts in a secure and structured way, you can use the Contacts
framework. To do this, ask the user for permission to read and write their contact data.
Getting permission to access Contact data
The first step is to configure the message that will be shown when requesting access to the user’s contacts information, which is done on the Info tab in your project settings, with the following permission key:
Privacy - Contacts Usage Description
The access point for reading, writing, and fetching contacts on the device is the CNContactStore
type. It represents the user’s contacts store database and provides you with the APIs to work with it.
import Contacts
private let contactStore = CNContactStore()
As the list of contacts in the user’s device is private, the user must authorize your app to access it. That is done through the requestAccess(for:)
method.
func requestAccessToContacts() async {
do {
let granted = try await contactStore.requestAccess(for: .contacts)
} catch {
print("Error requesting access: \(error.localizedDescription)")
}
}
You can also check the current status of the authorization with the authorizationStatus(for:)
method, which returns the current status as an CNAuthorizationStatus
value.
func statusLabel(for status: CNAuthorizationStatus) -> String {
switch status {
case .notDetermined: return "Not Determined"
case .restricted: return "Restricted"
case .denied: return "Denied"
case .authorized: return "Authorized"
case .limited: return "Limited"
@unknown default: return "Unknown"
}
}
Once you have everything set up, you are ready to start fetching contact information and using it within the context of your app.
Following is an example of an implementation of the topics discussed above in a sample SwiftUI app.
The following ViewModel
class is responsible for handling the logic for accessing contacts, and at the moment, it is just handling user authorization.
import Contacts
@MainActor
class ViewModel: ObservableObject {
@Published var permissionStatus: CNAuthorizationStatus = .notDetermined
@Published var permissionGranted: Bool = false
private let contactStore = CNContactStore()
init() {
updateAuthorizationStatus()
}
func updateAuthorizationStatus() {
permissionStatus = CNContactStore.authorizationStatus(for: .contacts)
permissionGranted = (permissionStatus == .authorized)
}
func requestPermission() async {
do {
let granted = try await contactStore.requestAccess(for: .contacts)
await MainActor.run {
self.permissionGranted = granted
self.permissionStatus = CNContactStore.authorizationStatus(for: .contacts)
}
} catch {
print("Error requesting access: \(error.localizedDescription)")
}
}
}
The following implementation used that ViewModel
to showcase the current status of the authorization.
import SwiftUI
import Contacts
struct ContentView: View {
@StateObject private var manager = ViewModel()
var body: some View {
Form {
Text("Permission Status: \(statusLabel(for: manager.permissionStatus))")
Button("Request Contact Access") {
Task {
await manager.requestPermission()
}
}
.buttonStyle(.automatic)
if manager.permissionGranted {
Text("Access Granted ✅")
.foregroundColor(.green)
} else if manager.permissionStatus == .denied {
Text("Access Denied ❌")
.foregroundColor(.red)
}
}
}
func statusLabel(for status: CNAuthorizationStatus) -> String {
switch status {
case .notDetermined: return "Not Determined"
case .restricted: return "Restricted"
case .denied: return "Denied"
case .authorized: return "Authorized"
case .limited: return "Limited"
@unknown default: return "Unknown"
}
}
}