Quyền truy cập danh bạ là một cơ chế của hệ điều hành di động yêu cầu sự đồng ý rõ ràng của người dùng trước khi đọc sổ địa chỉ của thiết bị. Trên iOS, việc truy cập danh bạ được thực hiện thông qua CNContactStore, trong khi trên Android sử dụng Contacts API và hệ thống quyền runtime. Theo Apple Developer Documentation, 2025, từ iOS 18, tất cả ứng dụng phải sử dụng Contacts Access API thống nhất. Việc triển khai đúng yêu cầu quyền làm tăng khả năng được phê duyệt bởi quy trình kiểm duyệt của cửa hàng ứng dụng.
Những điểm chính
Quyền truy cập danh bạ là một cơ chế của hệ điều hành bảo vệ sổ địa chỉ của người dùng khỏi bị đọc trái phép bởi ứng dụng bên thứ ba. Trong hệ điều hành di động, danh bạ được coi là dữ liệu nhạy cảm vì chúng chứa tên, số điện thoại, địa chỉ email và ảnh của những người trong mạng lưới của người dùng.
Trên iOS, quyền được quy định bởi Contacts framework và lớp CNContactStore. Người dùng nhìn thấy hộp thoại hệ thống ở lần yêu cầu truy cập đầu tiên, nơi họ có thể chọn cấp hoặc từ chối quyền truy cập. Trên Android, bảo vệ dựa trên hệ thống quyền runtime: ứng dụng khai báo READ_CONTACTS trong tệp kê khai và yêu cầu nó trong thời gian chạy thông qua ActivityResultLauncher hoặc fragment xử lý kết quả.
Theo Statista (2025), hơn 68% người dùng iOS và 54% người dùng Android từ chối quyền truy cập danh bạ ở lần yêu cầu đầu tiên. Điều này có nghĩa là nhà phát triển không chỉ cần triển khai yêu cầu một cách chính xác mà còn phải giải thích cho người dùng lý do cần truy cập.
Tiêu chuẩn ngành — chỉ yêu cầu truy cập khi chức năng thực sự cần thiết, không phải khi khởi chạy lần đầu. Cách tiếp cận này giảm tỷ lệ từ chối và cải thiện trải nghiệm người dùng.
Trong hệ sinh thái Apple, việc truy cập danh bạ được điều chỉnh bởi Contacts framework, được giới thiệu trong iOS 9. Lớp CNContactStore cung cấp các phương thức để yêu cầu quyền và thực hiện các thao tác đọc và ghi. Ở lần gọi requestAccess(for:) đầu tiên, hệ thống hiển thị hộp thoại gốc giải thích lý do truy cập.
Bắt đầu từ iOS 17, Apple đã giới thiệu chế độ truy cập một danh bạ. Người dùng có thể chọn một danh bạ từ sổ địa chỉ và chia sẻ nó với ứng dụng mà không tiết lộ toàn bộ cơ sở dữ liệu. Chế độ này được triển khai thông qua CNContactPickerViewController và không yêu cầu gọi requestAccess(for:).
Điều quan trọng là nhà phát triển phải hiểu: nếu ứng dụng yêu cầu quyền truy cập đầy đủ nhưng về mặt chức năng chỉ cần một danh bạ, người kiểm duyệt App Store có thể từ chối bản dựng. Theo Apple App Review Guidelines (2025), phần 5.1.1 yêu cầu rõ ràng lượng dữ liệu tối thiểu cần thiết.
Với việc phát hành iOS 18, Apple đã thắt chặt các yêu cầu đối với Privacy Manifest — tệp privacy.xcprivacy nơi nhà phát triển khai báo lý do truy cập dữ liệu được bảo vệ. Đối với danh bạ, khóa NSContactsUsageDescription được sử dụng với văn bản đã được bản địa hóa hiển thị trong hộp thoại hệ thống.
Nếu không có privacy manifest chính xác, ứng dụng không thể vượt qua quy trình kiểm duyệt App Store Connect. Văn bản mô tả phải cụ thể: không phải “Để cải thiện hiệu suất”, mà là “Để tìm bạn bè qua số điện thoại”.
Trên Android, quyền truy cập danh bạ được bảo vệ bởi quyền READ_CONTACTS, thuộc loại nguy hiểm — nó phải được yêu cầu trong thời gian chạy, không chỉ khi cài đặt. Cơ chế quyền runtime được giới thiệu trong Android 6.0 (API 23) và vẫn là cách chính để bảo vệ dữ liệu nhạy cảm.
Quyền READ_CONTACTS được khai báo trong tệp kê khai qua thẻ uses-permission và được yêu cầu trong mã nguồn thông qua ActivityResultLauncher hoặc fragment với onRequestPermissionsResult. Người dùng có thể từ chối yêu cầu hoặc chọn “Không hỏi lại”, sau đó ứng dụng phải xử lý việc từ chối một cách chính xác.
Bắt đầu từ Android 14 (API 34), hành vi của quyền runtime đã thay đổi: sau hai lần từ chối liên tiếp, hệ điều hành tự động đặt cờ neverAskAgain. Theo Google Developer Documentation (2024), nhà phát triển nên kiểm tra trạng thái qua shouldShowRequestPermissionRationale trước khi yêu cầu lại.
Để đọc danh bạ, Android sử dụng ContentProvider có tên ContactsContract. Đây là một cơ sở dữ liệu có cấu trúc có thể truy cập thông qua ContentResolver. Dữ liệu được tổ chức thành nhiều bảng: Contacts (danh bạ), RawContacts (bản ghi thô từ các tài khoản khác nhau), Data (thông tin chi tiết: điện thoại, email, địa chỉ).
Truy vấn ContactsContract được thực hiện qua URI ContactsContract.Contacts.CONTENT_URI. Nhà phát triển nên yêu cầu số lượng cột tối thiểu và sử dụng phép chiếu để lọc trường — điều này tăng tốc thực thi truy vấn và giảm tiêu thụ bộ nhớ.
Việc triển khai thực tế yêu cầu truy cập danh bạ khác nhau giữa iOS và Android. Dưới đây là các ví dụ cụ thể bằng Swift và Kotlin với xử lý tất cả các trạng thái quyền có thể có.
Trên iOS, yêu cầu được thực hiện thông qua phương thức requestAccess của lớp CNContactStore. Kết quả được trả về trong một closure với giá trị boolean và lỗi tùy chọn. Ví dụ dưới đây minh họa chu trình yêu cầu đầy đủ với xử lý trạng thái.
import Contacts
let store = CNContactStore()
store.requestAccess(for: .contacts) { granted, error in
if granted {
print("Đã cấp quyền truy cập danh bạ")
// Thực hiện các thao tác danh bạ
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("Truy cập bị từ chối: \(error?.localizedDescription ?? "lỗi không xác định")")
}
}
Trên Android, yêu cầu được thực hiện thông qua ActivityResultLauncher với hợp đồng RequestPermission. Ví dụ dưới đây cho thấy làm việc với ContactsContract.ContentProvider sau khi có quyền.
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", "Danh bạ: $name")
}
}
} else {
// Giải thích cho người dùng lý do cần truy cập
showRationaleDialog()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestPermissionLauncher.launch(android.Manifest.permission.READ_CONTACTS)
}
Các nhà phát triển ứng dụng di động giàu kinh nghiệm tuân theo một tập hợp các phương pháp đã được kiểm chứng khi làm việc với quyền truy cập danh bạ. Những quy tắc này giúp vượt qua quy trình kiểm duyệt của cửa hàng ứng dụng và duy trì lòng tin của người dùng. Tuân theo các phương pháp hay nhất giúp đơn giản hóa đáng kể quá trình xuất bản và bảo trì.
Không bao giờ yêu cầu quyền truy cập danh bạ khi khởi chạy ứng dụng lần đầu. Yêu cầu đầu tiên nên xảy ra trong bối cảnh của một chức năng cụ thể: tìm bạn bè, mời người tham gia, nhập danh bạ. Người dùng hiểu lý do của yêu cầu đồng ý nhiều hơn 2–3 lần, theo nghiên cứu của Apptentive (2024).
Nếu chức năng chỉ cần truy cập vào một danh bạ, hãy sử dụng CNContactPickerViewController trên iOS hoặc intent ngầm ACTION_PICK trên Android. Các phương pháp này không yêu cầu quyền trước và cho phép người dùng tự chọn bản ghi mà không tiết lộ toàn bộ sổ địa chỉ cho ứng dụng.
Ứng dụng phải xử lý chính xác các tình huống khi người dùng từ chối quyền truy cập. Trên iOS, hãy kiểm tra trạng thái qua CNContactStore.authorizationStatus(for:) và hướng người dùng đến Cài đặt nếu cần. Trên Android, sử dụng shouldShowRequestPermissionRationale để hiển thị giải thích bổ sung trước khi yêu cầu lại.
Không bao giờ hiển thị hộp thoại thứ hai ngay sau khi từ chối — điều này bị coi là hung hăng và làm giảm xếp hạng ứng dụng. Phương pháp hay nhất: sau một thời gian, hiển thị màn hình có giải thích và nút “Đi tới Cài đặt” mở màn hình quyền hệ thống thông qua Intent. Kiểm tra kịch bản từ chối trên thiết bị thực — trình mô phỏng không phải lúc nào cũng tái tạo chính xác hành vi của hộp thoại quyền hệ thống.
Câu hỏi thường gặp
Ứng dụng yêu cầu truy cập danh bạ cho các chức năng như tìm bạn bè, mời người tham gia, tự động điền biểu mẫu và đồng bộ với máy chủ. Ví dụ: ứng dụng nhắn tin tìm danh bạ theo số điện thoại, ứng dụng CRM nhập khách hàng.
Truy cập một danh bạ (iOS 17+) thông qua CNContactPickerViewController cho phép người dùng chọn một danh bạ mà không tiết lộ toàn bộ sổ địa chỉ. Truy cập đầy đủ cho phép ứng dụng đọc tất cả danh bạ thiết bị thông qua CNContactStore. Truy cập một danh bạ an toàn hơn và không yêu cầu chỉ định NSContactsUsageDescription trong privacy manifest.
Trên iOS, vào Cài đặt — Quyền riêng tư và Bảo mật — Danh bạ và tắt quyền truy cập cho ứng dụng cụ thể. Trên Android, mở Cài đặt — Ứng dụng — chọn ứng dụng — Quyền — Danh bạ và chọn “Từ chối”.
Privacy Manifest (tệp privacy.xcprivacy) là tài liệu bắt buộc cho iOS 18+, trong đó nhà phát triển khai báo lý do truy cập dữ liệu được bảo vệ, bao gồm danh bạ. Khóa NSContactsUsageDescription chứa mô tả đã được bản địa hóa hiển thị trong hộp thoại yêu cầu quyền hệ thống.
Android phân loại READ_CONTACTS là quyền nguy hiểm vì nó cấp quyền truy cập vào dữ liệu cá nhân của người dùng. Cơ chế quyền runtime, được giới thiệu trong Android 6.0, yêu cầu sự đồng ý rõ ràng trong thời gian chạy, không chỉ khi cài đặt ứng dụng.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm