Contacts Authorization

The ContactsManager SDK requires permission to access the user’s device contacts. This guide explains how to request and manage contacts access in your app.

Authorization Status

Check the current authorization status using the contactsAccessStatus property:

let status = ContactsService.shared.contactsAccessStatus

switch status {
case .notDetermined:
    print("User has not yet been asked for permission")
case .authorized:
    print("Access granted")
case .denied:
    print("Access denied")
case .restricted:
    print("Access restricted")
}

Requesting Access

Request access to contacts with the requestContactsAccess() method:

let granted = await ContactsService.shared.requestContactsAccess()

if granted {
    print("Contacts access granted")
    // Now you can use contacts features
} else {
    print("Contacts access denied")
    // Handle the denial case
}

Best Practices for Requesting Access

  1. Explain Why: Before requesting access, explain to users why your app needs contacts access
  2. Request at the Right Time: Only request access when you actually need it
  3. Handle Denial Gracefully: Provide a way for users to enable access later if they initially deny it

Handling Access Denial

When access is denied, the SDK provides a SwiftUI alert that guides users to the Settings app:

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Your content here")
        }
        // This will only show when access has been denied
        .overlay(ContactsService.shared.settingsAlert)
    }
}

You can also check if the settings alert should be shown:

if ContactsService.shared.contactsAccessStatus == .denied {
    // Show your custom UI to guide users to settings
}

Privacy Configuration

In addition to requesting permission at runtime, you must configure your app’s Info.plist file with a privacy description:

<key>NSContactsUsageDescription</key>
<string>This app needs access to your contacts to provide contact management and social features.</string>

This message will be shown to users when they are prompted for permission.

Syncing Contacts After Authorization

Once access is granted, you can sync contacts from the device to the local database:

do {
    let syncedCount = try await ContactsService.shared.syncContacts()
    print("Synced \(syncedCount) contacts")
} catch {
    print("Error syncing contacts: \(error.localizedDescription)")
}

The SDK automatically syncs contacts when:

  • The app becomes active
  • Contact store changes are detected
  • Contact access status changes from denied to granted

Complete Authorization Flow Example

Here’s a complete example showing the authorization flow:

import SwiftUI
import ContactsManager

struct ContactsView: View {
    @State private var isLoading = false
    @State private var contacts: [Contact] = []
    @State private var errorMessage: String?
    
    var body: some View {
        VStack {
            if isLoading {
                ProgressView("Loading contacts...")
            } else if let error = errorMessage {
                Text("Error: \(error)")
                    .foregroundColor(.red)
                Button("Try Again") {
                    checkContactsAccess()
                }
                .padding()
            } else if contacts.isEmpty {
                Text("No contacts found")
                    .foregroundColor(.gray)
            } else {
                List(contacts, id: \.id) { contact in
                    Text(contact.displayName ?? "Unknown")
                }
            }
        }
        .navigationTitle("Contacts")
        .onAppear {
            checkContactsAccess()
        }
        // Show settings alert if needed
        .overlay(ContactsService.shared.settingsAlert)
    }
    
    private func checkContactsAccess() {
        Task {
            let status = ContactsService.shared.contactsAccessStatus
            
            switch status {
            case .notDetermined:
                // Request access
                let granted = await ContactsService.shared.requestContactsAccess()
                if granted {
                    loadContacts()
                } else {
                    errorMessage = "Contacts access denied"
                }
                
            case .authorized:
                // Already have access, load contacts
                loadContacts()
                
            case .denied, .restricted:
                // Access denied or restricted
                errorMessage = "Please grant contacts access in Settings"
            }
        }
    }
    
    private func loadContacts() {
        isLoading = true
        errorMessage = nil
        
        Task {
            do {
                // First ensure contacts are synced
                _ = try await ContactsService.shared.syncContacts()
                
                // Then fetch contacts
                let fetchedContacts = try await ContactsService.shared.fetchContacts(
                    fieldType: .any
                )
                
                DispatchQueue.main.async {
                    self.contacts = fetchedContacts
                    self.isLoading = false
                }
            } catch {
                DispatchQueue.main.async {
                    self.errorMessage = error.localizedDescription
                    self.isLoading = false
                }
            }
        }
    }
}

Troubleshooting

Common Issues

  1. Permission Dialog Not Showing

    • Verify that you’ve added the NSContactsUsageDescription key to your Info.plist
    • Make sure you’re calling requestContactsAccess() on the main thread
  2. Permission Denied

    • Use the settings alert to guide users to enable access in Settings
    • Explain the benefits of contacts access clearly
  3. Sync Failing After Permission Granted

    • Make sure the SDK is properly initialized
    • Check for network connectivity issues
    • Verify that you’ve registered the contact source