Leo
Leo

Reputation: 1768

How to identify an EKCalendar to store a user calendar selection

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?

enter image description here

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

Answers (2)

Boon
Boon

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

geohei
geohei

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

Related Questions