Quyền truy cập lịch là một cơ chế của hệ điều hành di động bảo vệ dữ liệu lịch của người dùng khỏi việc đọc và sửa đổi trái phép. Trên iOS, quyền truy cập lịch được triển khai thông qua framework EventKit với các lớp EKEventStore và EKCalendar, còn trên Android — thông qua các quyền READ_CALENDAR và WRITE_CALENDAR kết hợp với CalendarContract API. Theo Apple Developer Documentation, 2025, để truy cập lịch trên iOS 18+ cần có yêu cầu rõ ràng thông qua hộp thoại hệ thống. EventKit cung cấp giao diện thống nhất để đọc và tạo sự kiện trên tất cả lịch đã kết nối.
Những điểm chính
Quyền truy cập lịch là một cơ chế bảo vệ dữ liệu cá nhân kiểm soát việc đọc và ghi sự kiện trong các ứng dụng lịch của thiết bị. Lịch chứa thông tin nhạy cảm: cuộc họp, thời hạn, lời nhắc và kế hoạch cá nhân — do đó hệ điều hành di động phân loại quyền truy cập vào nó là quan trọng.
Trên iOS, quyền truy cập lịch được điều chỉnh bởi framework EventKit. Ứng dụng có thể yêu cầu quyền đọc và ghi sự kiện, và người dùng có thể cấp hoặc từ chối yêu cầu thông qua hộp thoại hệ thống. Trên Android, sự bảo vệ dựa trên hai quyền runtime: READ_CALENDAR và WRITE_CALENDAR.
Theo một nghiên cứu của Pew Research Center (2024), khoảng 45% người dùng thiết bị di động sử dụng lịch thường xuyên và 62% trong số họ từ chối quyền truy cập cho các ứng dụng không giải thích lý do yêu cầu dữ liệu lịch.
Nguyên tắc chính — ứng dụng chỉ nên yêu cầu quyền truy cập cho các chức năng liên quan trực tiếp đến lịch: tạo lời nhắc, đồng bộ sự kiện, nhập lịch trình.
Trên iOS, quyền truy cập vào lịch và lời nhắc được cung cấp thông qua một framework duy nhất — EventKit. Lớp trung tâm EKEventStore quản lý tất cả các thao tác: yêu cầu quyền, đọc sự kiện, tạo và chỉnh sửa mục lịch. Khi gọi requestAccess(to:entityType:) lần đầu, hệ thống hiển thị hộp thoại gốc với lời giải thích.
Lớp EKEventStore là điểm truy cập vào hệ thống con lịch của iOS. Để yêu cầu truy cập, bạn phải gọi phương thức requestAccess(to: .event), truyền loại thực thể (sự kiện hoặc lời nhắc). Sau khi nhận được quyền, EKEventStore cung cấp quyền truy cập vào tất cả lịch được kết nối với iCloud, Google, Exchange và các nhà cung cấp khác.
Một đặc điểm quan trọng: EKEventStore là một đối tượng nặng — việc tạo nó tốn thời gian và tiêu tốn tài nguyên. Nên khởi tạo nó một lần và tái sử dụng trong suốt vòng đời ứng dụng. Theo WWDC Session 10117 (2024), Apple khuyến nghị lưu bộ nhớ đệm phiên bản EventStore để tối ưu hiệu suất.
iOS không tách biệt quyền đọc và ghi lịch — người dùng cấp toàn quyền truy cập hoặc từ chối. Tuy nhiên, ứng dụng có thể kiểm soát thao tác ở cấp mã: đọc sự kiện qua EKEventStore.event, tạo qua EKEventStore.save và xóa qua EKEventStore.remove. Từ iOS 18, có thể yêu cầu quyền truy cập chỉ vào một loại thực thể cụ thể — .event hoặc .reminder.
Trong iOS 17+, Apple đã giới thiệu cơ chế truy cập tạm thời: một số ứng dụng có thể nhận quyền truy cập trong 24 giờ sau khi người dùng xác nhận một lần. Tính năng này đặc biệt hữu ích cho các ứng dụng cần quyền truy cập lịch một lần — ví dụ: để nhập lịch hội nghị.
Trên Android, quyền truy cập lịch được bảo vệ bởi hai quyền riêng biệt: READ_CALENDAR và WRITE_CALENDAR. Cả hai đều thuộc danh mục nguy hiểm và yêu cầu runtime request. Việc tách biệt đọc và ghi cho phép người dùng tinh chỉnh mức độ truy cập của ứng dụng.
Quyền READ_CALENDAR cho phép ứng dụng đọc sự kiện từ tất cả lịch của người dùng, bao gồm tên, thời gian, người tham gia và mô tả. Quyền WRITE_CALENDAR cho phép tạo, sửa đổi và xóa sự kiện. Cả hai được chỉ định trong tệp kê khai qua thẻ uses-permission và được yêu cầu trong thời gian chạy qua ActivityResultLauncher.
Bắt đầu từ Android 14 (API 34), hệ thống cảnh báo người dùng nếu ứng dụng yêu cầu cả hai quyền cùng lúc. Nên yêu cầu chúng riêng biệt: đầu tiên READ_CALENDAR để đọc, sau đó WRITE_CALENDAR khi lần đầu tạo sự kiện. Theo Google I/O 2024, cách tiếp cận này giảm tỷ lệ từ chối xuống 23%.
CalendarContract là ContentProvider của Android cấu trúc dữ liệu lịch thành các bảng quan hệ. Các bảng chính bao gồm: Calendars (danh sách lịch), Events (sự kiện), Attendees (người tham gia), Reminders (thông báo). Quyền truy cập dữ liệu được thực hiện qua ContentResolver.query() với URI và phép chiếu được chỉ định.
Để chèn sự kiện mới, bạn cần sử dụng ContentValues chỉ định lịch, thời gian bắt đầu và kết thúc, tiêu đề và mô tả. CalendarContract hỗ trợ múi giờ, sự kiện lặp lại và lời nhắc với khoảng thông báo tùy chỉnh.
Việc triển khai yêu cầu truy cập lịch cần xem xét các tính năng cụ thể của nền tảng. Dưới đây là các ví dụ bằng Swift và Kotlin minh họa cách làm việc đúng với EventKit và CalendarContract.
Yêu cầu truy cập lịch iOS được thực hiện qua phương thức requestAccess của lớp EKEventStore. Ví dụ dưới đây cho thấy cách tạo sự kiện sau khi nhận được quyền.
import EventKit
let eventStore = EKEventStore()
eventStore.requestAccess(to: .event) { granted, error in
guard granted else {
print("Truy cập lịch bị từ chối")
return
}
let event = EKEvent(eventStore: eventStore)
event.title = "Cuộc họp nhóm"
event.startDate = Date()
event.endDate = Date(timeIntervalSinceNow: 3600)
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
print("Sự kiện đã được tạo: \(event.eventIdentifier)")
} catch {
print("Lỗi lưu: \(error.localizedDescription)")
}
}
Trên Android, yêu cầu quyền READ_CALENDAR và WRITE_CALENDAR được thực hiện qua ActivityResultLauncher. Ví dụ cho thấy cách đọc sự kiện từ lịch của người dùng sau khi nhận được quyền truy cập.
val calendarPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (permissions[Manifest.permission.READ_CALENDAR] == true) {
val uri = CalendarContract.Events.CONTENT_URI
val projection = arrayOf(
CalendarContract.Events.TITLE,
CalendarContract.Events.DTSTART,
CalendarContract.Events.DTEND
)
val cursor = contentResolver.query(uri, projection, null, null, null)
cursor?.use {
val titleIndex = it.getColumnIndex(CalendarContract.Events.TITLE)
while (it.moveToNext()) {
Log.d("Lịch", "Sự kiện: ${it.getString(titleIndex)}")
}
}
} else {
// Hiển thị giải thích và đề xuất vào cài đặt
requestPermissionSettingsRedirect()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
calendarPermissionLauncher.launch(
arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR)
)
}
Làm việc với quyền lịch đòi hỏi một chiến lược được cân nhắc kỹ lưỡng, xem xét các yêu cầu của cả hai nền tảng và kỳ vọng của người dùng. Làm theo các khuyến nghị dưới đây giúp vượt qua quá trình kiểm duyệt và tăng tỷ lệ cấp quyền.
Chỉ yêu cầu truy cập lịch khi người dùng thực hiện hành động cần dữ liệu lịch: “Thêm vào lịch”, “Đồng bộ lịch trình”, “Nhập sự kiện”. Hộp thoại trước quyền (hộp thoại tùy chỉnh trước hộp thoại hệ thống) tăng tỷ lệ đồng ý lên 35%, theo Localytics (2024).
Trên iOS, sử dụng khóa NSCalendarsUsageDescription trong tệp kê khai quyền riêng tư với văn bản cụ thể. Thay vì “Để tạo sự kiện”, hãy viết “Để thêm bài tập vào lịch của bạn”. Cách diễn đạt cụ thể làm tăng tỷ lệ chuyển đổi yêu cầu lên 20–30%.
Nếu người dùng từ chối yêu cầu, không hiển thị hộp thoại hệ thống lại — điều này sẽ kích hoạt neverAskAgain trên Android hoặc làm hộp thoại không khả dụng trên iOS. Thay vào đó, hãy đề xuất chuyển đến cài đặt qua Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) trên Android hoặc UIApplication.openSettingsURLString trên iOS.
Khi vào lại màn hình, hãy kiểm tra trạng thái quyền. Trên iOS, gọi EKEventStore.authorizationStatus(for: .event) và cập nhật giao diện theo trạng thái hiện tại. Trên Android, sử dụng ContextCompat.checkSelfPermission() để kiểm tra trạng thái hiện tại và quyết định có hiển thị nút chuyển đến cài đặt hay không.
Câu hỏi thường gặp
Ứng dụng yêu cầu quyền truy cập lịch để tạo sự kiện, đồng bộ lịch trình, nhập thời hạn và tích hợp với lời nhắc. Ví dụ: trình theo dõi sức khỏe thêm bài tập, ứng dụng lập kế hoạch tạo nhiệm vụ và ứng dụng du lịch nhập chuyến bay vào lịch của người dùng.
READ_CALENDAR cung cấp quyền đọc tất cả sự kiện và lịch của người dùng. WRITE_CALENDAR cho phép tạo, sửa đổi và xóa sự kiện. Người dùng có thể cấp một quyền mà không cần quyền kia, mang lại khả năng kiểm soát linh hoạt mức độ truy cập của ứng dụng vào dữ liệu lịch.
Mở Cài đặt — Quyền riêng tư và bảo mật — Lịch. Chọn ứng dụng và tắt công tắc truy cập. Ứng dụng sẽ mất khả năng đọc và tạo sự kiện cho đến lần yêu cầu rõ ràng và xác nhận tiếp theo của người dùng.
Ứng dụng sẽ không thể đọc hoặc tạo sự kiện. Phương thức requestAccess sẽ trả về granted = false trên iOS hoặc checkSelfPermission sẽ trả về PERMISSION_DENIED trên Android. Nhà phát triển nên triển khai suy giảm nhẹ nhàng — ứng dụng tiếp tục hoạt động mà không có chức năng lịch mà không bị treo hoặc hiển thị lỗi.
Trên iOS, điều này không khả thi — EventKit yêu cầu quyền truy cập đầy đủ cho bất kỳ thao tác nào với sự kiện. Trên Android, bạn có thể sử dụng Intent.ACTION_INSERT để tạo sự kiện qua ứng dụng lịch hệ thống, không yêu cầu quyền runtime, nhưng cũng không cho phép đọc sự kiện hiện có.
Tóm tắ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