Discover Calendar and EventKit
System & Services 进阶 20m

探索日历集成:EventKit 与 EventKitUI 实战

Discover Calendar and EventKit

2023年6月5日

在 Apple 官方观看视频

一句话判断

想让自己的 App 和系统日历打通?这场 Session 给出了从添加事件到实现虚拟会议扩展的完整路线图。

这场 Session 讲了什么

Adam 从 EventKit 和 EventKitUI 两个框架出发,详细讲解了 App 如何与系统日历集成。核心内容分成四个方向:

添加事件。最简单的方式是用 EKEventEditViewController,在 iOS 17 中它运行在独立进程里,不需要请求日历权限。你只需创建事件、填好属性、弹出编辑器,用户点”添加”就完事了。更轻量的方式是 Siri Event Suggestions API,适合餐厅预订、机票、演出票这类场景,事件会出现在日历的收件箱里。

读取和修改事件。需要 full access 权限。iOS 17 引入了新的权限模型,分为无权限、只写、完全访问三个级别。

虚拟会议扩展。如果你的 App 支持音视频通话,可以通过 Virtual Conference Extension 让日历 App 直接展示你的会议链接和品牌信息,还能从日历直接跳转到你的 App。

EventKitUI 的三个视图控制器EKEventEditViewController(编辑事件)、EKEventViewController(查看事件详情)、EKCalendarChooser(选择日历),覆盖了日历交互的主要场景。

值得深挖的点

iOS 17 的进程隔离改动EKEventEditViewController 现在在独立进程中运行,这意味着你不需要请求日历权限就能用它添加事件。这是一个重要的隐私改进——用户通过系统 UI 确认后事件才会被写入,App 本身无法直接访问日历数据。

日期计算的坑。Session 特别强调要用 Foundation 的 CalendarDateComponents 来做日期运算,而不是手动计算秒数。夏令时切换会让简单的时间差计算出错,这是很多开发者踩过的坑。

三级权限模型。无权限 -> 只写 -> 完全访问,每一级对应不同的 API 能力。只写权限意味着你可以添加事件但不能读取已有事件,这对于预订类 App 来说恰到好处。

代码片段

// 使用 EventKitUI 添加事件(iOS 17 无需请求权限)
let eventStore = EKEventStore()

let event = EKEvent(eventStore: eventStore)
event.title = "产品评审会"
event.startDate = Calendar.current.date(from: DateComponents(
    year: 2024, month: 1, day: 15, hour: 14, minute: 0
))!
// 用 DateComponents 计算结束时间,避免手动加减秒数
event.endDate = Calendar.current.date(
    byAdding: .hour, value: 2, to: event.startDate
)!

let editor = EKEventEditViewController()
editor.event = event
editor.eventStore = eventStore
editor.delegate = self
present(editor, animated: true)
// 使用 Siri Event Suggestions 添加预订事件
let reference = INSpeakableString(
    vocabularyIdentifier: "reservation-123",
    spokenPhrase: "今晚的晚餐预订"
)
let dateRange = INDateComponentsRange(
    start: startDateComponents, end: endDateComponents
)
let location = CLPlacemark(
    location: CLLocation(latitude: 39.9, longitude: 116.4),
    name: "某餐厅"
)
let reservation = INRestaurantReservation(
    itemReference: reference,
    reservationNumber: "R-123",
    bookingTime: Date(),
    reservationStatus: .confirmed,
    reservationDuration: dateRange,
    partySize: 4,
    restaurantLocation: location
)

最佳实践

  • 事件标题要简洁,它会出现在小组件和通知中,过长的标题会被截断。
  • 位置信息尽量提供完整地址或 MapKit handle,这样系统才能提供”出发时间”提醒和 Maps 建议。
  • 如果只是添加事件,优先使用 EKEventEditViewController 或 Siri Event Suggestions,避免直接请求日历权限。
  • 每个 App 只应创建一个 EKEventStore 实例,不要到处 new。
  • 对于虚拟会议,实现 Virtual Conference Extension 可以让你的服务在日历 App 中获得品牌曝光。

还有什么值得关注

  • “DropInLessons” 示例代码中的完整实现值得参考
  • iOS 17 的进程隔离机制同样影响其他系统 UI 组件
  • 如果你做的是旅行类 App,Siri Event Suggestions 的航班和租车支持可能直接用得上
  • EKCalendarChooser 的多选模式适合自定义日历视图的场景
WWDC 2023