Please note that I am attempting to add an appointment on an iPhone 13 Pro Max simulator, where I am not able to create a new event using XCode 13.
I have been basing my code on this sample,
https://github.com/richardtop/CalendarApp
Here is the source code for CalendarViewController.swift
, where I think the createNewEvent
is a key function:
import UIKit
import CalendarKit
import EventKit
import EventKitUI
final class CalendarViewController: DayViewController, EKEventEditViewDelegate {
private var eventStore = EKEventStore()
override func viewDidLoad() {
super.viewDidLoad()
title = "Calendar"
// The app must have access to the user's calendar to show the events on the timeline
requestAccessToCalendar()
// Subscribe to notifications to reload the UI when
subscribeToNotifications()
}
private func requestAccessToCalendar() {
// Request access to the events
eventStore.requestAccess(to: .event) { [weak self] granted, error in
// Handle the response to the request.
DispatchQueue.main.async {
guard let self = self else { return }
self.initializeStore()
self.subscribeToNotifications()
self.reloadData()
}
}
}
private func subscribeToNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(storeChanged(_:)),
name: .EKEventStoreChanged,
object: eventStore)
}
private func initializeStore() {
eventStore = EKEventStore()
}
@objc private func storeChanged(_ notification: Notification) {
reloadData()
}
// MARK: - DayViewDataSource
// This is the `DayViewDataSource` method that the client app has to implement in order to display events with CalendarKit
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
// The `date` always has it's Time components set to 00:00:00 of the day requested
let startDate = date
var oneDayComponents = DateComponents()
oneDayComponents.day = 1
// By adding one full `day` to the `startDate`, we're getting to the 00:00:00 of the *next* day
let endDate = calendar.date(byAdding: oneDayComponents, to: startDate)!
let predicate = eventStore.predicateForEvents(withStart: startDate, // Start of the current day
end: endDate, // Start of the next day
calendars: nil) // Search in all calendars
let eventKitEvents = eventStore.events(matching: predicate) // All events happening on a given day
let calendarKitEvents = eventKitEvents.map(EKWrapper.init)
return calendarKitEvents
}
// MARK: - DayViewDelegate
// MARK: Event Selection
override func dayViewDidSelectEventView(_ eventView: EventView) {
guard let ckEvent = eventView.descriptor as? EKWrapper else {
return
}
presentDetailViewForEvent(ckEvent.ekEvent)
}
private func presentDetailViewForEvent(_ ekEvent: EKEvent) {
let eventController = EKEventViewController()
eventController.event = ekEvent
eventController.allowsCalendarPreview = true
eventController.allowsEditing = true
navigationController?.pushViewController(eventController,
animated: true)
}
// MARK: Event Editing
override func dayView(dayView: DayView, didLongPressTimelineAt date: Date) {
// Cancel editing current event and start creating a new one
endEventEditing()
let newEKWrapper = createNewEvent(at: date)
create(event: newEKWrapper, animated: true)
}
private func createNewEvent(at date: Date) -> EKWrapper {
let newEKEvent = EKEvent(eventStore: eventStore)
newEKEvent.calendar = eventStore.defaultCalendarForNewEvents
var components = DateComponents()
components.hour = 1
let endDate = calendar.date(byAdding: components, to: date)
newEKEvent.startDate = date
newEKEvent.endDate = endDate
newEKEvent.title = "New event"
let newEKWrapper = EKWrapper(eventKitEvent: newEKEvent)
newEKWrapper.editedEvent = newEKWrapper
return newEKWrapper
}
override func dayViewDidLongPressEventView(_ eventView: EventView) {
guard let descriptor = eventView.descriptor as? EKWrapper else {
return
}
endEventEditing()
beginEditing(event: descriptor,
animated: true)
}
override func dayView(dayView: DayView, didUpdate event: EventDescriptor) {
guard let editingEvent = event as? EKWrapper else { return }
if let originalEvent = event.editedEvent {
editingEvent.commitEditing()
if originalEvent === editingEvent {
// If editing event is the same as the original one, it has just been created.
// Showing editing view controller
presentEditingViewForEvent(editingEvent.ekEvent)
} else {
// If editing event is different from the original,
// then it's pointing to the event already in the `eventStore`
// Let's save changes to oriignal event to the `eventStore`
try! eventStore.save(editingEvent.ekEvent,
span: .thisEvent)
}
}
reloadData()
}
private func presentEditingViewForEvent(_ ekEvent: EKEvent) {
let eventEditViewController = EKEventEditViewController()
eventEditViewController.event = ekEvent
eventEditViewController.eventStore = eventStore
eventEditViewController.editViewDelegate = self
present(eventEditViewController, animated: true, completion: nil)
}
override func dayView(dayView: DayView, didTapTimelineAt date: Date) {
endEventEditing()
}
override func dayViewDidBeginDragging(dayView: DayView) {
endEventEditing()
}
// MARK: - EKEventEditViewDelegate
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
endEventEditing()
reloadData()
controller.dismiss(animated: true, completion: nil)
}
}
It gets to the point where I can add an appointment, but the Add
button in the top RHS does not work, and I am unable to edit the New event
text.
https://i.stack.imgur.com/XvZ3s.jpg
The issue for me is not so much the warning. I just want to be able to create an appointment using CalendarKit
. Does anyone have any suggestions? TIA.
The issue is that I chose
Device
->Erase All Content and Settings...
after giving permission to access my calendar in the simulator but when I debugged the app again after that, so I guess that means it loses access to my calendar, but it didn't request access to the calendar again for some reason.When I tested on a different device it requested permission on the first run and things worked properly after that.