Contacts Permission in Mobile Development — What It Is, How It Works, and Requesting Access

Author: IT Sectr Published: 2026-05-22 Reading time: 8 min

Contacts permission is a mechanism of mobile OSes that requires explicit user consent before reading the device’s address book. On iOS, access to contacts is handled through CNContactStore, while on Android it uses the Contacts API and runtime permissions system. According to Apple Developer Documentation, 2025, starting with iOS 18 all apps must use the unified Contacts Access API. Proper implementation of the permission request increases the chances of approval by app store moderation.

Key Takeaways

  • Contacts Permission — a required permission for reading user contacts on iOS and Android.
  • CNContactStore — the main iOS class for accessing the address book via the Contacts framework.
  • Runtime permission — the Android model where the READ_CONTACTS permission is requested at runtime.
  • Privacy manifest — a required file for iOS 18+ describing the reason for contacts access.
  • Single contact access — an iOS 17+ mode that lets users grant access to one contact without fully exposing the address book.

What Is Contacts Permission?

Contacts permission is an operating system mechanism that protects the user’s address book from unauthorized reading by third-party apps. In mobile OSes, contacts are considered sensitive data because they contain names, phone numbers, email addresses, and photos of people in the user’s network.

On iOS, the permission is governed by the Contacts framework and the CNContactStore class. The user sees a system dialog on the first access request, where they can choose to grant or deny access. On Android, protection is built on the runtime permissions system: the app declares READ_CONTACTS in the manifest and requests it at runtime via ActivityResultLauncher or a fragment handling the result.

According to Statista (2025), over 68% of iOS users and 54% of Android users deny contacts access on the first request. This means developers need not only to implement the request correctly but also to explain to the user why access is necessary.

Industry standard — request access only when the functionality is actually needed, not on first launch. This approach reduces denial rates and improves user experience.

How Contact Access Request Works on iOS

In the Apple ecosystem, access to contacts is regulated by the Contacts framework, introduced in iOS 9. The CNContactStore class provides methods for requesting permission and performing read and write operations. On the first call to requestAccess(for:), the system displays a native dialog explaining the reason for access.

CNContactStore and Single Contact Access

Starting with iOS 17, Apple introduced single contact access mode. Users can select one contact from the address book and share it with the app without revealing the entire database. This mode is implemented via CNContactPickerViewController and does not require calling requestAccess(for:).

It is important for developers to understand: if an app requests full access but functionally only needs one contact, App Store moderators may reject the build. According to Apple App Review Guidelines (2025), section 5.1.1 explicitly requires the minimum necessary amount of data.

Privacy Manifest in iOS 18

With the release of iOS 18, Apple tightened requirements for the Privacy Manifest — the privacy.xcprivacy file where developers declare the reason for accessing protected data. For contacts, the key NSContactsUsageDescription is used with localized text displayed in the system dialog.

Without a correct privacy manifest, the app cannot pass App Store Connect moderation. The description text should be specific: not “To improve performance,” but “To find friends by phone number.”

How Contact Access Request Works on Android

On Android, access to contacts is protected by the READ_CONTACTS permission, which belongs to the dangerous category — it must be requested at runtime, not just during installation. The runtime permissions mechanism was introduced in Android 6.0 (API 23) and remains the primary way to protect sensitive data.

READ_CONTACTS and Runtime Permission

The READ_CONTACTS permission is declared in the manifest via the uses-permission tag, and requested in code through ActivityResultLauncher or a fragment with onRequestPermissionsResult. The user may deny the request or select “Never ask again,” after which the app must properly handle the denial.

Starting with Android 14 (API 34), the behavior of runtime permissions changed: after two consecutive denials, the OS automatically sets the neverAskAgain flag. According to Google Developer Documentation (2024), developers should check the status via shouldShowRequestPermissionRationale before re-requesting.

ContactsContract and ContentProvider

For reading contacts, Android uses a ContentProvider called ContactsContract. This is a structured database accessible through ContentResolver. Data is organized into several tables: Contacts (contacts), RawContacts (raw records from different accounts), Data (detailed information: phones, emails, addresses).

Querying ContactsContract is done via the URI ContactsContract.Contacts.CONTENT_URI. Developers should request the minimum number of columns and use projection to filter fields — this speeds up query execution and reduces memory consumption.

Code Examples for Requesting Contacts

The practical implementation of requesting contacts access differs on iOS and Android. Below are specific examples in Swift and Kotlin with handling of all possible permission states.

Requesting Access in Swift

On iOS, the request is made via the requestAccess method of the CNContactStore class. The result is returned in a closure with a boolean value and an optional error. The example below demonstrates the full request cycle with status handling.

swift
import Contacts

let store = CNContactStore()

store.requestAccess(for: .contacts) { granted, error in
    if granted {
        print("Contacts access granted")
        // Contact operations execution
        let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]
        let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
        try? store.enumerateContacts(with: request) { contact, stop in
            print("\(contact.givenName) \(contact.familyName)")
        }
    } else {
        print("Access denied: \(error?.localizedDescription ?? "unknown error")")
    }
}

Requesting Access in Kotlin

On Android, the request is made via ActivityResultLauncher with the RequestPermission contract. The example below shows working with ContactsContract.ContentProvider after obtaining permission.

kotlin
val requestPermissionLauncher =
    registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
        if (isGranted) {
            val uri = ContactsContract.Contacts.CONTENT_URI
            val cursor = contentResolver.query(uri, null, null, null, null)
            cursor?.use {
                val nameIndex = it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
                while (it.moveToNext()) {
                    val name = it.getString(nameIndex)
                    Log.d("Contacts", "Contact: $name")
                }
            }
        } else {
            // Explain the reason for needing access to the user
            showRationaleDialog()
        }
    }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestPermissionLauncher.launch(android.Manifest.permission.READ_CONTACTS)
}

Best Practices for Working with Contacts

Experienced mobile app developers follow a set of proven practices when working with contacts permission. These rules help pass app store moderation and maintain user trust. Following best practices significantly simplifies the publishing and maintenance process.

Minimize Requested Permissions

Never request contacts access on the first app launch. The first request should occur in the context of a specific feature: finding friends, inviting participants, importing contacts. A user who understands the reason for the request agrees 2–3 times more often, according to Apptentive (2024) research.

If the functionality only needs access to a single contact, use CNContactPickerViewController on iOS or an implicit intent ACTION_PICK on Android. These methods do not require prior permission and let users select a record themselves without exposing the entire address book to the app.

Handling Access Denial

The app must properly handle situations where the user denies access. On iOS, check the status via CNContactStore.authorizationStatus(for:) and direct the user to Settings if needed. On Android, use shouldShowRequestPermissionRationale to show additional explanation before re-requesting.

Never show a second dialog immediately after a denial — this is perceived as aggressive and lowers the app’s rating. Best practice: after some time, show a screen with an explanation and a “Go to Settings” button that opens the system permissions screen via Intent. Test the denial scenario on real devices — simulators do not always correctly reproduce the behavior of system permission dialogs.

Frequently Asked Questions

Why does an app need access to contacts?

Apps request contacts access for features like finding friends, inviting participants, auto-filling forms, and syncing with a server. Examples: messengers look up contacts by phone number, CRM apps import clients.

What is the difference between single and full access on iOS?

Single contact access (iOS 17+) via CNContactPickerViewController lets users pick one contact without revealing the entire address book. Full access allows the app to read all device contacts through CNContactStore. Single contact access is more secure and does not require specifying NSContactsUsageDescription in the privacy manifest.

How to revoke contacts permission?

On iOS, go to Settings — Privacy & Security — Contacts and disable access for the specific app. On Android, open Settings — Apps — select the app — Permissions — Contacts and choose “Deny.”

What is a privacy manifest in iOS 18?

Privacy Manifest (privacy.xcprivacy file) is a mandatory document for iOS 18+, in which developers declare the reasons for accessing protected data, including contacts. The NSContactsUsageDescription key contains a localized description displayed in the system permission request dialog.

Why does Android require explicit READ_CONTACTS request?

Android classifies READ_CONTACTS as a dangerous permission because it grants access to the user’s personal data. The runtime permissions mechanism, introduced in Android 6.0, requires explicit consent at runtime, not just during app installation.

Summary

  • Contacts Permission — a required permission for reading the user’s address book on iOS and Android.
  • CNContactStore — the main iOS class for requesting access and performing contacts operations via the Contacts framework.
  • Runtime permission — the Android 6.0+ mechanism requiring explicit READ_CONTACTS request at runtime.
  • Privacy manifest — a required file for iOS 18+ declaring the reasons for contacts access.
  • Single contact access — an iOS 17+ feature that lets users pick one contact without fully exposing the database.
  • Data minimization — request access only when actually needed, not on first launch.
  • Handling denial — the app should respond gracefully to permission denial without crashing or repeatedly prompting.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also