Swift 4 Как получить все события из календаря?

Я использую Swift 4.1. И я хочу написать функцию, которая будет собирать все события из всех календарей в приложении Calendar iOS. Благодаря этому ответу на stackoverflow: Как получить все события вне календаря (Swift) я смог написать свой собственный class и назвать его Cale. Пожалуйста, посмотрите на это:

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")
        }
    }
}

Довольно простой класс, пользоваться им можно, он работает, но с одним исключением. Основная функция get(). В этой функции я создаю предикат на основе двух дат: начала и конца. Как видите, дата начала:

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

и дата окончания:

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

Но если мы запустим программу, то увидим, что предикат будет таким:

CADEventPredicate start: 01.01.2016, 03:00; конец: 01.01.2020, 03:00; кал :( 2)

Всего с 2016 по 2020 4 года! Я тестировал его в разные даты, но смог получить предикат с интервалом максимум 4 года. Значит, он не даст мне всех событий! Итак, вопрос: как получить все события из календаря? Если можно, без дат!

Спасибо за любую помощь или совет в будущем!


person Alex    schedule 20.07.2018    source источник
comment
я столкнулся с этой проблемой в iOS 12   -  person GvSharma    schedule 26.09.2018
comment
Даже у меня такая же проблема, кто-нибудь решил эту проблему?   -  person Arshad Shaik    schedule 15.03.2019
comment
Я знаю только, как обойти эту проблему, но полное решение, боюсь, невозможно.   -  person Alex    schedule 15.03.2019
comment
есть ли способ управлять?   -  person Arshad Shaik    schedule 15.03.2019
comment
Например, мы хотим оживить информацию на 100 лет. С 1970 по 2070 год. Сомневаюсь, что у кого-то могут быть более долгосрочные планы. Итак, просто организуйте получение всех событий с 1970 по 2070 год за каждые 4 года. Поместите все события в массив и удалите дубликаты!   -  person Alex    schedule 15.03.2019


Ответы (2)


В случае, если кому-то все еще интересно, Apple намеренно ограничила это четырьмя годами.

Из predicateForEvents (withStart: end: calendars :):

По соображениям производительности этот метод сопоставляет только те события в течение четырехлетнего промежутка времени. Если диапазон дат между startDate и endDate превышает четыре года, он сокращается до первых четырех лет.

Если вам нужен более длинный диапазон, я думаю, вам придется обернуть его своей собственной функцией, чтобы разделить его на четыре года.

person Cameron Stone    schedule 31.05.2020
comment
Спасибо, это новая информация. Каждый должен написать свое собственное решение, чтобы получить полный календарь. Я предпочитаю брать 100 лет. - person Alex; 04.06.2020
comment
Спасатель жизни. Я нашел ваш ответ после того, как часами пытался понять, почему он всегда возвращает 0 элементов, когда я пытался получить все данные из хранилища событий с предикатом из Date.distantPast и Date.distantFuture. Я должен уделить больше внимания разделу обсуждения ???? - person JsW; 14.01.2021

Потратив столько времени, я нашел решение своей проблемы. Моя проблема заключалась в следующем:

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

предикат, как показано ниже,

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

Поскольку я из Индии, предикат добавляет GMT + 5: 30 к моему фактическому времени начала и окончания события. У меня есть метод переполнения стека для преобразования в локальный и глобальный часовой пояс, как показано ниже.

    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)
    }
}

Передавая дату начала и окончания со временем для предиката, я конвертирую в globalTimeZone, как показано ниже.

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

Если у вас нет времени, у вас есть только даты начала и окончания, затем используйте предикат, как показано ниже,

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)

Потому что, если у вас нет времени и преобразования в globalTimeZone, вы можете столкнуться с проблемами при удалении событий.

Надеюсь, это кому-нибудь поможет.

person Arshad Shaik    schedule 15.03.2019
comment
Ваши мероприятия длятся меньше года. Пост был о том, как получать ВСЕ события из календаря. - person Alex; 15.03.2019
comment
вы можете проверить, используя метод convert to globalTimeZone, проверить дату и время предиката и сообщить мне. - person Arshad Shaik; 15.03.2019