-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathBMEventManager.swift
More file actions
133 lines (107 loc) · 4.96 KB
/
BMEventManager.swift
File metadata and controls
133 lines (107 loc) · 4.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//
// BMEventManager.swift
// berkeley-mobile
//
// Created by Shawn Huang on 10/31/20.
// Copyright © 2020 ASUC OCTO. All rights reserved.
//
import EventKit
import Foundation
class BMEventManager {
private let eventStore = EKEventStore()
private let queue = DispatchQueue(label: "com.berkeleymobile.eventstore", qos: .userInitiated)
/// A dictionary to keep track if events exist in Calendar. Keys are `BMEventCalendarEntry`'s `uniqueIdentifier`.
private(set) var doesEventsExistInCalendarDict: [String: Bool] = [:]
func addEventToCalendar(calendarEvent: BMCalendarEvent) async throws {
try await eventStore.requestAccess(to: .event)
try await saveEvent(calendarEvent)
if let event = calendarEvent as? BMEventCalendarEntry {
doesEventsExistInCalendarDict[event.uniqueIdentifier] = true
}
}
func deleteEvent(_ calendarEvent: BMCalendarEvent) async throws {
let matchingExistingEvents = await getMatchingEvents(for: calendarEvent)
guard let eventToDelete = matchingExistingEvents.first(where: makeEventMatcher(for: calendarEvent)) else {
throw BMError.unableToFindEventInCalendar
}
try eventStore.remove(eventToDelete, span: .thisEvent)
if let event = calendarEvent as? BMEventCalendarEntry {
doesEventsExistInCalendarDict.removeValue(forKey: event.uniqueIdentifier)
}
}
func doesEventExists(for calendarEvent: BMCalendarEvent) async -> Bool {
let matchingExistingEvents = await getMatchingEvents(for: calendarEvent)
let eventAlreadyExists = matchingExistingEvents.contains(where: makeEventMatcher(for: calendarEvent))
return eventAlreadyExists
}
func processEventsExistenceInCalendar(for events: [BMEventCalendarEntry]) async {
let results: [(BMEventCalendarEntry, Bool)] = await withTaskGroup(of: (BMEventCalendarEntry, Bool).self) { group in
for event in events {
group.addTask {
let exists = await self.doesEventExists(for: event)
return (event, exists)
}
}
var acc: [(BMEventCalendarEntry, Bool)] = []
for await pair in group { acc.append(pair) }
return acc
}
for (event, exists) in results {
doesEventsExistInCalendarDict[event.uniqueIdentifier] = exists
}
}
// MARK: - Private Methods
private func makeEventMatcher(for calendarEvent: BMCalendarEvent) -> (EKEvent) -> Bool {
return { event in
event.title == calendarEvent.name &&
event.startDate == calendarEvent.startDate &&
event.endDate == calendarEvent.endDate
}
}
private func getMatchingEvents(for calendarEvent: BMCalendarEvent) async -> [EKEvent] {
await withCheckedContinuation { continuation in
_getMatchingEvents(for: calendarEvent) { continuation.resume(returning: $0) }
}
}
private func _getMatchingEvents(for calendarEvent: BMCalendarEvent, completion: @escaping ([EKEvent]) -> Void) {
guard let endDate = Calendar.current.date(byAdding: .year, value: 1, to: calendarEvent.startDate) else {
completion([])
return
}
queue.async { [eventStore] in
let predicate = eventStore.predicateForEvents(withStart: calendarEvent.startDate, end: endDate, calendars: nil)
let matchingEvents = eventStore.events(matching: predicate)
completion(matchingEvents)
}
}
private func saveEvent(_ calendarEvent: BMCalendarEvent) async throws {
let eventAlreadyExists = await doesEventExists(for: calendarEvent)
guard !eventAlreadyExists else {
throw BMError.eventAlreadyAddedInCalendar
}
let event = getEKEventFromCalendarEvent(for: calendarEvent)
do {
try eventStore.save(event, span: .thisEvent)
let authStatus = EKEventStore.authorizationStatus(for: .event)
if authStatus == .writeOnly {
throw BMError.mayExistedInCalendarAlready
}
} catch {
if let ekError = error as? EKError, ekError.errorCode == 1 {
throw BMError.insufficientAccessToCalendar
} else {
throw error
}
}
}
private func getEKEventFromCalendarEvent(for calendarEvent: BMCalendarEvent) -> EKEvent {
let event = EKEvent(eventStore: eventStore)
event.title = calendarEvent.name
event.startDate = calendarEvent.startDate
event.endDate = calendarEvent.endDate
event.location = calendarEvent.location
event.notes = calendarEvent.additionalDescription + (calendarEvent.descriptionText ?? "")
event.calendar = eventStore.defaultCalendarForNewEvents
return event
}
}