Search code examples
iosswiftsortingcalendareventkit

Swift: Date sorting EKEvents after fetching from EKEventStore into tableView data


Introduction

Context:

I'm working on a little app in which I'm using the EKEventStore to get the users events. I'm fetching all EKEvents for the next 10 days (from when the user opens the app) and storing them into an array of EKEvents. I then would like to sort them up (see below) and use them as data for my tableView calendar.

Issues:

  • I want the following structure of my array: var allDayEvents : [[EKEvent]]? and var dateIntervalEvents : [[EKEvent]]?. Where there is a static 11 index (0-10).

  • I believe I'm making this much more code filled than it has to be. There must be a quicker way both for me and performance wise.

Question:

  • How do I sort the my fetched events from the EventStore query to the syntax for allDayEvents and dateIntervalEvents in an efficient way?
    1. I need the dates sorted so the earliest is first in each section of the arrays.

Clarification of tableView structure:

  1. I have 11 sections which corresponds to the next 11 days. (So it is a static value of 11 sections)
  2. I want to display the events that is on the date the section provides.
  3. Also events that are not allDay events are to be displayed first in the section in one cell type and then the events that are all day are displayed in another cell type after.
  4. Giving room for the following syntax on my data: dateIntervalEvents[indexPath.section][indexPath.row] and allDayEvents[indexPath.section][indexPath.row]

Code:

  • my fetch method.

        var allDayEvents : [EKEvent]?
        var dateIntervalEvents : [EKEvent]?
    
        private func getEventsFromCalendarStore() {
    
            let eventStore = EKEventStore()
            var fetchedEvents : [EKEvent]?
            //simulator? either way wrong dates are fetched
            guard let startDate = Calendar.current.date(byAdding: .day, value: -1, to: Date()) else { return }
            guard let endDate = Calendar.current.date(byAdding: .day, value: 10, to: startDate) else { return }
    
            let eventPredicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: userCalendars)
            fetchedEvents = eventStore.events(matching: eventPredicate)
    
            //Basically how to sort fetchedEvents to get the required structure of my array.
    
        }
    
  • My attempt at sorting the array:

    private func getEventsFromCalendarStore() {
    
         let eventStore = EKEventStore()
         var fetchedEvents : [EKEvent] = []
         var allDayers : [EKEvent] = []
         var dateIntervalls : [EKEvent] = []
    
        var allDay1 : [EKEvent] = []
         var allDay2 : [EKEvent] = []
         var allDay3 : [EKEvent] = []
         var allDay4 : [EKEvent] = []
         var allDay5 : [EKEvent] = []
         var allDay6 : [EKEvent] = []
         var allDay7 : [EKEvent] = []
         var allDay8 : [EKEvent] = []
         var allDay9 : [EKEvent] = []
         var allDay10 : [EKEvent] = []
         var allDay11 : [EKEvent] = []
    
         var dIDay1 : [EKEvent] = []
         var dIDay2 : [EKEvent] = []
         var dIDay3 : [EKEvent] = []
         var dIDay4 : [EKEvent] = []
         var dIDay5 : [EKEvent] = []
         var dIDay6 : [EKEvent] = []
         var dIDay7 : [EKEvent] = []
         var dIDay8 : [EKEvent] = []
         var dIDay9 : [EKEvent] = []
         var dIDay10 : [EKEvent] = []
         var dIDay11 : [EKEvent] = []
    
         guard let startDate = Calendar.current.date(byAdding: .day, value: -1, to: Date()) else { return }
         guard let endDate = Calendar.current.date(byAdding: .day, value: 10, to: startDate) else { return }
    
         let eventPredicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: userCalendars)
    
         //getting all events and sorting them in date order.
         fetchedEvents = eventStore.events(matching: eventPredicate).sorted(by: { (e1 : EKEvent, e2 : EKEvent) -> Bool in
             return e1.compareStartDate(with: e2) == .orderedAscending
         })
    
         //This cant be best practice of sorting it the following way?
         print(fetchedEvents)
    
         for event in fetchedEvents {
    
             if event.isAllDay == true {
                 allDayers.append(event)
             } else {
               dateIntervalls.append(event)
             }
         }
    
         for event in dateIntervalls {
    
             if event.startDate == startDate {
                 dIDay1.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay2.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay3.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay4.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay5.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay6.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay7.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay8.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay9.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay10.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 dIDay11.append(event)
             }
    
         }
    
         for event in allDayers {
             if event.startDate == startDate {
                 allDay1.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay2.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay3.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay4.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay5.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay6.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay7.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay8.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay9.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay10.append(event)
             } else if event.startDate == Calendar.current.date(byAdding: .day, value: 1, to: startDate) {
                 allDay11.append(event)
             }
         }
    
         allDayEvents?.insert(allDay1, at: 0)
         allDayEvents?.insert(allDay2, at: 1)
         allDayEvents?.insert(allDay3, at: 2)
         allDayEvents?.insert(allDay4, at: 3)
         allDayEvents?.insert(allDay5, at: 4)
         allDayEvents?.insert(allDay6, at: 5)
         allDayEvents?.insert(allDay7, at: 6)
         allDayEvents?.insert(allDay8, at: 7)
         allDayEvents?.insert(allDay9, at: 8)
         allDayEvents?.insert(allDay10, at: 9)
         allDayEvents?.insert(allDay11, at: 10)
    
         dateIntervalEvents?.insert(dIDay1, at: 0)
         dateIntervalEvents?.insert(dIDay2, at: 1)
         dateIntervalEvents?.insert(dIDay3, at: 2)
         dateIntervalEvents?.insert(dIDay4, at: 3)
         dateIntervalEvents?.insert(dIDay5, at: 4)
         dateIntervalEvents?.insert(dIDay6, at: 5)
         dateIntervalEvents?.insert(dIDay7, at: 6)
         dateIntervalEvents?.insert(dIDay8, at: 7)
         dateIntervalEvents?.insert(dIDay9, at: 8)
         dateIntervalEvents?.insert(dIDay10, at: 9)
         dateIntervalEvents?.insert(dIDay11, at: 10)
    
    }
    

How would you approach the sorting for this kind of array structure?

Thanks for reading my post.


Solution

  • First, get rid of your two sets of 11 arrays. Replace each with a nested array.

    Next, after you populate allDayers and dateIntervals, you should sort them.

    Then you can iterate each event in the two arrays, calculate the number of days difference from the start date, and update the corresponding nested array.

    private func getEventsFromCalendarStore() {
        guard let startDate = Calendar.current.date(byAdding: .day, value: -1, to: Date()) else { return }
        guard let endDate = Calendar.current.date(byAdding: .day, value: 10, to: startDate) else { return }
    
        let eventStore = EKEventStore()
        var fetchedEvents : [EKEvent] = []
        var allDayers : [EKEvent] = []
        var dateIntervals : [EKEvent] = []
    
        let eventPredicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: userCalendars)
    
        //getting all events and sorting them in date order.
        fetchedEvents = eventStore.events(matching: eventPredicate).sorted(by: { (e1 : EKEvent, e2 : EKEvent) -> Bool in
            return e1.compareStartDate(with: e2) == .orderedAscending
        })
    
        //This cant be best practice of sorting it the following way?
        print(fetchedEvents)
    
        for event in fetchedEvents {
            if event.isAllDay == true {
                allDayers.append(event)
            } else {
                dateIntervals.append(event)
            }
        }
    
        allDayers.sort { $0.startDate < $1.startDate  }
        dateIntervals.sort { $0.startDate < $1.startDate  }
    
        var groupedAllDayers = Array(repeating: [EKEvent](), count: 11)
        var groupedIntervals = Array(repeating: [EKEvent](), count: 11)
    
        for event in dateIntervals {
            let days = Calendar.current.dateComponents([.day], from: startDate, to: event.startDate).day!
            groupedIntervals[days].append(event)
        }
        for event in allDayers {
            let days = Calendar.current.dateComponents([.day], from: startDate, to: event.startDate).day!
            groupedAllDayers[days].append(event)
        }
    }
    

    At the end of this, groupedAllDayers and groupedIntervals will contains your two sets of events, each grouped by the number of days from the startDate to the event's startDate.