iOS 16.4下EventKit无法通过eventIdentifier找到已创建事件
iOS 16.4 EventKit Exchange日历事件ID问题解析
问题1:为何创建事件后eventIdentifier会变化?
这是Exchange等同步型日历账户的特性:
- 创建事件时,EventKit会先生成一个本地临时ID用于本地操作;
- 当设备与Exchange服务器完成同步后,服务器会为事件分配一个正式的全局唯一ID,并替换本地临时ID;
- 真机环境下会触发真实的Exchange同步流程,所以ID会被更新;而模拟器通常没有真实的Exchange同步环境,因此保留本地临时ID。
- 你创建后立即获取的是本地临时ID,后续遍历日历拿到的是同步完成后的服务器正式ID,二者格式自然不同。另外你输出的ID末尾带有
\0空字符,这是字符串编码问题,也会导致后续检索失败。
问题2:如何获取正确的eventIdentifier用于后续操作?
可以通过以下两种可靠方式获取:
方式1:监听同步完成通知,获取最新ID
注册EKEventStoreChangedNotification通知,当EventKit与服务器同步完成后,再去获取事件的最新ID:
// 在合适的生命周期节点注册通知 NotificationCenter.default.addObserver(forName: .EKEventStoreChanged, object: eventStore, queue: .main) { [weak self] _ in guard let self = self, let calendarId = self.calendar else { return } let calendar = self.eventStore.calendar(withIdentifier: calendarId)! // 用事件的特征属性(如标题、时间范围)查询 let predicate = self.eventStore.predicateForEvents(withStart: self.workout.viewStart.addingTimeInterval(-300), end: self.workout.viewEnd.addingTimeInterval(300), calendars: [calendar]) let events = self.eventStore.events(matching: predicate) if let targetEvent = events.first(where: { $0.title.hasPrefix("SweatPlan: ") && $0.startDate == self.workout.viewStart }) { let correctId = targetEvent.eventIdentifier?.trimmingCharacters(in: .controlCharacters) // 保存correctId用于后续更新/删除操作 } } // 创建事件的原有逻辑保持不变,无需立刻保存ID
方式2:通过事件特征属性查询获取最新ID
如果不想监听通知,可以在创建事件后,延迟一段时间(确保同步完成),通过事件的唯一特征(标题、起止时间、所属日历)查询事件,拿到正式ID:
eventStore.requestAccess(to: .event) { granted, _ in guard granted, let calendarForEvent = eventStore.calendar(withIdentifier: calendar) else { return } // 创建事件逻辑 let event = EKEvent(eventStore: eventStore) event.calendar = calendarForEvent event.title = "SweatPlan: \(workout.viewTitle)" event.startDate = workout.viewStart event.endDate = workout.viewEnd do { try eventStore.save(event, span: .thisEvent, commit: true) // 延迟查询,给同步留足时间 DispatchQueue.main.asyncAfter(deadline: .now() + 2) { let predicate = eventStore.predicateForEvents(withStart: workout.viewStart.addingTimeInterval(-300), end: workout.viewEnd.addingTimeInterval(300), calendars: [calendarForEvent]) let events = eventStore.events(matching: predicate) if let targetEvent = events.first(where: { $0.title == event.title && $0.startDate == event.startDate }) { let correctId = targetEvent.eventIdentifier?.trimmingCharacters(in: .controlCharacters) completion(correctId ?? "") } } } catch { fatalError(error.localizedDescription) } }
额外注意点
- 必须清理ID中的控制字符(如末尾的
\0),使用trimmingCharacters(in: .controlCharacters)可以去除这类无效字符; - 不要依赖创建后立即返回的
eventIdentifier作为持久化ID,仅同步型日历会出现此问题,本地/iCloud日历通常不会有ID变更情况。
内容的提问来源于stack exchange,提问作者Timo Puschkasch
相关产品推荐
相关产品推荐

