3

I am using Swift 4.1. And I want to write a function which will collect all events from all calendars in Calendar app of iOS. Thanks to this answer on stackoverflow: How to get all Events out of a Calendar (Swift) I was able to write my own class and call it Cale. Please, look at this:

import UIKit
import EventKit

class Cale {

    private func createDate(year: Int) -> Date {
        var components = DateComponents()
        components.year = year
        components.timeZone = TimeZone(secondsFromGMT: 0)

        return Calendar.current.date(from: components)!
    }

    private let eventStore = EKEventStore()

    private func get() {
        let calendars = eventStore.calendars(for: .event)

        for calendar in calendars {
            // This checking will remove Birthdays and Hollidays callendars
            guard calendar.allowsContentModifications else {
                continue
            }

            let start = createDate(year: 2016)
            let end = createDate(year: 2025)

            print("start: \(start)")
            print("  end: \(end)")

            let predicate = eventStore.predicateForEvents(withStart: start, end: end, calendars: [calendar])

            print("predicate: \(predicate)")

            let events = eventStore.events(matching: predicate)

            for event in events {
                print("    title: \(event.title!)")
                print("startDate: \(event.startDate!)")
                print("  endDate: \(event.endDate!)")
            }
        }
    }

    func checkStatusAndGetAllEvents() {
        let currentStatus = EKEventStore.authorizationStatus(for: EKEntityType.event)

        switch currentStatus {
        case .authorized:
            //print("authorized")
            self.get()
        case .notDetermined:
            //print("notDetermined")
            eventStore.requestAccess(to: .event) { accessGranted, error in
                if accessGranted {
                    self.get()
                } else {
                    print("Change Settings to Allow Access")
                }
            }
        case .restricted:
            print("restricted")
        case .denied:
            print("denied")
        }
    }
}

Quite simple class, you can use it, it is working but with one exception. The main function there is get() In this function I am creating predicate based on two dates: start and end. As you can see start date is:

2016-01-01 00:00:00 +0000

and end date is:

2025-01-01 00:00:00 +0000

But if we run the program we will see that predicate will be like this:

CADEventPredicate start:01/01/2016, 03:00; end:01/01/2020, 03:00; cals:( 2 )

Only from 2016 to 2020, 4 years! I have tested it on different dates, but I could get predicate with 4 years interval maximum. It means, it will not give me all events! So question is: How to get all events from calendar? If it possible, without using dates!

Thank you for any future help or advice!

Alex
  • 1,038
  • 2
  • 12
  • 32

2 Answers2

3

In case anyone is still wondering, Apple limited this to four years intentionally.

From predicateForEvents(withStart:end:calendars:):

For performance reasons, this method matches only those events within a four year time span. If the date range between startDate and endDate is greater than four years, it is shortened to the first four years.

If you want a range longer than that, I guess you'll have to wrap this in your own function to split it into four year chunks.

Cameron Stone
  • 857
  • 1
  • 8
  • 10
  • Thank you, it's new information. Everybody should write their own solution to get whole calendar. I prefer to take 100 years. – Alex Jun 04 '20 at 11:13
  • Life saver. I found your answer after struggled for hours trying to figure out why it always returns 0 items when I tried to fetch all the data from the event store with predicate from Date.distantPast and Date.distantFuture. I should pay more attention to the discuss section – JsW Jan 14 '21 at 07:31
0

After spending so much time, i got a solution to my problem. My problem was as below,

event start date 2019-03-15 09:00:00 +0000
event end date 2019-03-15 14:00:00 +0000

the predicate as below,

CADEventPredicate start:15/03/19, 1:30 PM; end:15/03/19, 7:30 PM; cals:(null)

As i am from India, the predicate is adding GMT+5:30 to my actual event start and end time. I got a method from stack overflow, to convert to local & global timezone as below.

    extension Date {
    // Convert local time to UTC (or GMT)
    func toGlobalTime() -> Date {
        let timezone = TimeZone.current
        let seconds = -TimeInterval(timezone.secondsFromGMT(for: self))
        return Date(timeInterval: seconds, since: self)
    }

    // Convert UTC (or GMT) to local time
    func toLocalTime() -> Date {
        let timezone = TimeZone.current
        let seconds = TimeInterval(timezone.secondsFromGMT(for: self))
        return Date(timeInterval: seconds, since: self)
    }
}

While passing start and end date with time to predicate i am converting to globalTimeZone as below.

let predicate = eventStore.predicateForEvents(withStart: eventStartDateTime.toGlobalTime(), end: eventEndDateTime.toGlobalTime(), calendars: nil)

If you don't have time, you have only start & end dates, then use predicate as below,

event start date 2019-03-15 00:00:00 +0000
event end date 2019-03-15 00:00:00 +0000

let predicate = eventStore.predicateForEvents(withStart: eventStartDateTime, end: eventEndDateTime, calendars: nil)

Because if you don't have time and converting to globalTimeZone, you may face issues in deleting the events.

I hope it will help some one.

Arshad Shaik
  • 1,095
  • 12
  • 18