Reputation: 1768
When working with the users calendar, I implemented an EKCalendarChooser which allows the user to select multiple of his calendars. The selected calendar instances are retrieved just fine. Now I later want to use this selection but how can I store it permanently?
My first approach was to use the calendars identifier and store them as a string array to UserDefaults like
@State private var calendarSelection: [EKCalendar]
// my approach to convert the calendar selection into a storable format (string array of ids)
var selectedIds = [String]()
for calendar in calendarSelection {
selectedIds.append(calendar.calendarIdentifier)
}
// now store the string-array, eg. to user defaults:
UserDefaults.standard.set(selectedIds, forKey: "cids")
Unfortunately this doesn't work, because the calendarIdentifier
is not a permanent identifier and thus does change over time. As apple states in their documentation:
A full sync with the calendar will lose this identifier. You should have a plan for dealing with a calendar whose identifier is no longer fetch-able by caching its other properties.
How can the user's selection of his calendars be stored then?
Upvotes: 3
Views: 718
Reputation: 181
I too have the same problem. The bad thing is you cannot tell when the identifier will change. Here is my work around...
I store calendar identifier, calendar title, calendar source in userDefaults
I fetch a set of calendar by comparing identifier first. If identifier is not present in eventStore, then comparing the title AND source.
Of course, you can add more properties but title + source should be enough in my opinion.
I have another part in my code that update new identifier in userDefaults.
if userSettings.selectedCalID != [] { //check if the array is empty or not. If empty then do not use pre-selected calendar.
var selectedCalendarIdentifier : [EKCalendar] = []
for i in userSettings.selectedCalID{
print(i)
if let eventStoreID = eventStore.calendar(withIdentifier: i){
selectedCalendarIdentifier.append(eventStoreID)
} else {
//This is part where calendar identifier has changed or not present in eventstore. Retrive new identifier by checking calendar title AND calendar source.
if userSettings.selectedCalTitle != [] && userSettings.selectedCalSource != [] {
let calTitle = userSettings.selectedCalTitle[userSettings.selectedCalID.firstIndex(of: i) ?? 0]
let calSource = userSettings.selectedCalSource[userSettings.selectedCalID.firstIndex(of: i) ?? 0]
print("\(calTitle) - \(calSource)")
if let eventStoreCalNewID = eventStore.calendars(for: .event).filter({($0.title == calTitle) && ($0.source.title == calSource)}).first?.calendarIdentifier{
if let eventStoreID = eventStore.calendar(withIdentifier: eventStoreCalNewID){
selectedCalendarIdentifier.append(eventStoreID)
}
}
}
}
}
chooser.selectedCalendars = Set(selectedCalendarIdentifier)
}
Upvotes: 2
Reputation: 802
Ok, this works for me now.
I store the calendar identifier ...
func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
...
// store selected calendar identifier
selectedCalendar = calendarChooser.selectedCalendars.first?.calendarIdentifier
...
}
... then restore it.
func showCalendarChooser() {
...
// check if calendar identifier still exists
if let selectedCalendarIdentifier = eventStore.calendar(withIdentifier: selectedCalendar) {
//select stored calendar
calendarChooser.selectedCalendars = [selectedCalendarIdentifier]
}
...
}
This is for 1 selected calendar only (selectionStyle: .single
).
Changing this code to multiple calendars should be trivial.
I also tested if the calendar identifier might change ... no joy so far. It remained unchanged during all the tests. If it should change (nil
), no calendar will be selected and the app won't crash.
HTH
Upvotes: 0