Skip to content

Instantly share code, notes, and snippets.

@Fedenieto90
Last active March 29, 2023 17:26
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save Fedenieto90/95de03d6f002a79adf54da7b8e7ae72d to your computer and use it in GitHub Desktop.
Save Fedenieto90/95de03d6f002a79adf54da7b8e7ae72d to your computer and use it in GitHub Desktop.
import UIKit
import EventKit
import EventKitUI
typealias EventsCalendarManagerResponse = (_ result: ResultCustomError<Bool, CustomError>) -> Void
class EventsCalendarManager: NSObject {
var eventStore: EKEventStore!
override init() {
eventStore = EKEventStore()
}
// Request access to the Calendar
private func requestAccess(completion: @escaping EKEventStoreRequestAccessCompletionHandler) {
eventStore.requestAccess(to: EKEntityType.event) { (accessGranted, error) in
completion(accessGranted, error)
}
}
// Get Calendar auth status
private func getAuthorizationStatus() -> EKAuthorizationStatus {
return EKEventStore.authorizationStatus(for: EKEntityType.event)
}
// Check Calendar permissions auth status
// Try to add an event to the calendar if authorized
func addEventToCalendar(event: Event, completion : @escaping EventsCalendarManagerResponse) {
let authStatus = getAuthorizationStatus()
switch authStatus {
case .authorized:
self.addEvent(event: event, completion: { (result) in
switch result {
case .success:
completion(.success(true))
case .failure(let error):
completion(.failure(error))
}
})
case .notDetermined:
//Auth is not determined
//We should request access to the calendar
requestAccess { (accessGranted, error) in
if accessGranted {
self.addEvent(event: event, completion: { (result) in
switch result {
case .success:
completion(.success(true))
case .failure(let error):
completion(.failure(error))
}
})
} else {
// Auth denied, we should display a popup
completion(.failure(.calendarAccessDeniedOrRestricted))
}
}
case .denied, .restricted:
// Auth denied or restricted, we should display a popup
completion(.failure(.calendarAccessDeniedOrRestricted))
}
}
// Generate an event which will be then added to the calendar
private func generateEvent(event: Event) -> EKEvent {
let newEvent = EKEvent(eventStore: eventStore)
newEvent.calendar = eventStore.defaultCalendarForNewEvents
newEvent.title = event.name
newEvent.startDate = event.dateStart
newEvent.endDate = event.dateEnd
// Set default alarm minutes before event
let alarm = EKAlarm(relativeOffset: TimeInterval(Configuration.addEventToCalendarAlarmMinutesBefore()*60))
newEvent.addAlarm(alarm)
return newEvent
}
// Try to save an event to the calendar
private func addEvent(event: Event, completion : @escaping EventsCalendarManagerResponse) {
let eventToAdd = generateEvent(event: event)
if !eventAlreadyExists(event: eventToAdd) {
do {
try eventStore.save(eventToAdd, span: .thisEvent)
} catch {
// Error while trying to create event in calendar
completion(.failure(.eventNotAddedToCalendar))
}
completion(.success(true))
} else {
completion(.failure(.eventAlreadyExistsInCalendar))
}
}
// Check if the event was already added to the calendar
private func eventAlreadyExists(event eventToAdd: EKEvent) -> Bool {
let predicate = eventStore.predicateForEvents(withStart: eventToAdd.startDate, end: eventToAdd.endDate, calendars: nil)
let existingEvents = eventStore.events(matching: predicate)
let eventAlreadyExists = existingEvents.contains { (event) -> Bool in
return eventToAdd.title == event.title && event.startDate == eventToAdd.startDate && event.endDate == eventToAdd.endDate
}
return eventAlreadyExists
}
// Show event kit ui to add event to calendar
func presentCalendarModalToAddEvent(event: Event, completion : @escaping EventsCalendarManagerResponse) {
let authStatus = getAuthorizationStatus()
switch authStatus {
case .authorized:
presentEventCalendarDetailModal(event: event)
completion(.success(true))
case .notDetermined:
//Auth is not determined
//We should request access to the calendar
requestAccess { (accessGranted, error) in
if accessGranted {
self.presentEventCalendarDetailModal(event: event)
completion(.success(true))
} else {
// Auth denied, we should display a popup
completion(.failure(.calendarAccessDeniedOrRestricted))
}
}
case .denied, .restricted:
// Auth denied or restricted, we should display a popup
completion(.failure(.calendarAccessDeniedOrRestricted))
}
}
// Present edit event calendar modal
func presentEventCalendarDetailModal(event: Event) {
let event = generateEvent(event: event)
let eventModalVC = EKEventEditViewController()
eventModalVC.event = event
eventModalVC.eventStore = eventStore
eventModalVC.editViewDelegate = self
if let rootVC = UIApplication.shared.keyWindow?.rootViewController {
rootVC.present(eventModalVC, animated: true, completion: nil)
}
}
}
// EKEventEditViewDelegate
extension EventsCalendarManager: EKEventEditViewDelegate {
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
controller.dismiss(animated: true, completion: nil)
}
}
@PiyushPriyadarshiSigma
Copy link

PiyushPriyadarshiSigma commented Mar 24, 2020

update for swift 5, for the post https://medium.com/@fede_nieto/manage-calendar-events-with-eventkit-and-eventkitui-with-swift-74e1ecbe2524

  • // ERROR: Use of undeclared type 'CustomError'

// Add this ENUM

enum CustomError: Error {
    case calendarAccessDeniedOrRestricted
    case eventNotAddedToCalendar
    case eventAlreadyExistsInCalendar
}

  • // ERROR: Use of undeclared type 'ResultCustomError'

// Update Callback Argument to "Result" instead of "ResultCustomError"
typealias EventsCalendarManagerResponse = (_ result: Result<Bool, CustomError >) -> Void

  • // ERROR: Use of undeclared type 'Event'

// Replace all "Event" Type to "EKEvent" Type
Example:
Instead of private func generateEvent(event: Event) -> EKEvent {
write private func generateEvent(event: EKEvent) -> EKEvent {

  • // ERROR: 'keyWindow' was deprecated in iOS 13.0: Should not be used for applications that support multiple scenes as it returns a key window across all connected scenes

// Update the following code

let keyWindow = UIApplication.shared.connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
        
        if let rootVC = keyWindow?.rootViewController {
            rootVC.present(eventModalVC, animated: true, completion: nil)
        }

instead of


if let rootVC = UIApplication.shared.keyWindow?.rootViewController {
            rootVC.present(eventModalVC, animated: true, completion: nil)
        }

@likil33
Copy link

likil33 commented Apr 12, 2021

Screenshot 2021-04-12 at 4 16 41 PM

@likil33
Copy link

likil33 commented Apr 12, 2021

How to solve this? 👆🏻
Please help me

@Fedenieto90
Copy link
Author

Fedenieto90 commented Apr 12, 2021

Try with this below, not sure if it works but give it a try @likil33

//  Result.swift
//
//
import Foundation

// See https://github.com/antitypical/Result
enum Result<T, E: Swift.Error> {
    case success(T)
    case failure(E)

    public func dematerialize() throws -> T {
        switch self {
        case let .success(value):
            return value
        case let .failure(error):
            throw error
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment