How to use EKCalendarChooser with SwiftUI

Short post showing how to bridge this controller with SwiftUI and get selected calendars.

Just like other UIKit controllers EKCalendarChooser which lets users select their calendars in use in your app needs a small wrapper around it with the UIViewControllerRepresentable protocol.

This code below is intended as a starting point for folks who need to work with EKCalendarChooser in their SwiftUI project.

Big thanks to Paul Hudson for this guide about using UIImagePickerController with SwiftUI which served as an inspiration.

So here is the code:

import SwiftUI
import EventKitUI

struct CalendarChooser: UIViewControllerRepresentable {
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }

    @Environment(\.presentationMode) var presentationMode
    @Binding var calendars: Set<EKCalendar>?

    let eventStore: EKEventStore

    func makeUIViewController(context: UIViewControllerRepresentableContext<CalendarChooser>) -> UINavigationController {
        let chooser = EKCalendarChooser(selectionStyle: .multiple, displayStyle: .allCalendars, entityType: .event, eventStore: eventStore)
        chooser.selectedCalendars = calendars ?? []
        chooser.delegate = context.coordinator
        chooser.showsDoneButton = true
        chooser.showsCancelButton = true
        return UINavigationController(rootViewController: chooser)
    }

    func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<CalendarChooser>) {
    }

    class Coordinator: NSObject, UINavigationControllerDelegate, EKCalendarChooserDelegate {
        let parent: CalendarChooser

        init(_ parent: CalendarChooser) {
            self.parent = parent
        }

        func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
            parent.calendars = calendarChooser.selectedCalendars
            parent.presentationMode.wrappedValue.dismiss()
        }

        func calendarChooserDidCancel(_ calendarChooser: EKCalendarChooser) {
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
}

Sample usage can look something like this:

.sheet(isPresented: $showingCalendarChooser) {
                CalendarChooser(calendars: self.$eventsRepository.selectedCalendars, eventStore: self.eventsRepository.eventStore)
}

You need to pass in Binding for selected calendars and also instance of EKEventStore.

I am using this definition for selectedCalendars:

@Published var selectedCalendars: Set<EKCalendar>?

For more on EventKit check out other posts in this series or my example project on GitHub.

Uses: Xcode 11 & Swift 5