richardtop / calendarapp Goto Github PK
View Code? Open in Web Editor NEW๐ Reference implementation of a Calendar App with CalendarKit (template repository)
Home Page: https://www.youtube.com/watch?v=cJ63-_z1qg8
License: MIT License
๐ Reference implementation of a Calendar App with CalendarKit (template repository)
Home Page: https://www.youtube.com/watch?v=cJ63-_z1qg8
License: MIT License
when press on event I got nil as he can't cast from EventDescriptor to EkWrapper any help please `
//
// CalendarViewController.swift
// Servbud provider
//
// Created by Khaled on 22/12/2021.
// Copyright ยฉ 2021 ORGANIZATIONNAME. All rights reserved.
//
import UIKit
import CalendarKit
import EventKit
import EventKitUI
import LKAlertController
protocol CalendarDisplayLogic: AnyObject {
func displayAvailableTimes(times: [[MainNetworkModels.AvailableTimes]])
func displayError(errorMessage: String)
func displayUnAuthorized()
}
class CalendarViewController: DayViewController, CalendarDisplayLogic, EKEventEditViewDelegate {
// MARK: - Properties
typealias Models = CalendarModels
var router: (NSObjectProtocol & CalendarRoutingLogic & CalendarDataPassing)?
var interactor: CalendarBusinessLogic?
private var eventStore = EKEventStore()
let apiDate = DateFormatter()
let currentDate = Calendar.current.dateComponents([.year, .month], from: Date())
var currentmonth = Calendar.current.dateComponents([.year, .month], from: Date()).month
var allTimes: [MainNetworkModels.AvailableTimes] = []
var events = [Event]()
// MARK: - Object lifecycle
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - Setup
private func setup() {
let viewController = self
let interactor = CalendarInteractor()
let presenter = CalendarPresenter()
let router = CalendarRouter()
viewController.router = router
viewController.interactor = interactor
interactor.presenter = presenter
presenter.viewController = viewController
router.viewController = viewController
}
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
apiDate.dateFormat = "yyyy-MM-dd HH:mm:ss"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupUI()
requestAccessToCalendar()
view.showActivityView()
interactor?.getAvailableTimes(month: String(currentDate.month ?? 1), year: String(currentDate.year ?? 2022))
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
events.removeAll()
allTimes.removeAll()
}
func setupUI(){
title = "My Calender"
}
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.reloadData()
}
}
}
private func initializeStore() {
eventStore = EKEventStore()
}
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
events.removeAll()
allTimes.forEach { time in
let fromStringString = (time.from?.date ?? "") + " " + (time.from?.time ?? "")
let toStringString = (time.to?.date ?? "") + " " + (time.to?.time ?? "")
let newEvent = Event()
newEvent.dateInterval = DateInterval(start: apiDate.date(from: fromStringString) ?? Date(), end: apiDate.date(from: toStringString) ?? Date())
newEvent.isAllDay = false
newEvent.color = .random
newEvent.backgroundColor = .random
newEvent.text = "Avabilty \n from: " + (time.from?.time ?? "") + "to: " + (time.to?.time ?? "")
newEvent.textColor = .white
events.append(newEvent)
}
return events
}
// 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, to endDate: Date) -> EKWrapper {
let newEKEvent = EKEvent(eventStore: eventStore)
newEKEvent.calendar = eventStore.defaultCalendarForNewEvents
newEKEvent.startDate = date
newEKEvent.endDate = endDate
newEKEvent.title = "Availabilty"
let newEKWrapper = EKWrapper(eventKitEvent: newEKEvent)
newEKWrapper.editedEvent = newEKWrapper
return newEKWrapper
}
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 = "Availabilty"
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 {
presentEditingViewForEvent(editingEvent.ekEvent)
} else {
}
}
events.removeAll()
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()
events.removeAll()
reloadData()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let startDate = dateFormatter.string(from: controller.event?.startDate ?? Date())
dateFormatter.dateFormat = "HH:mm"
let startTime = dateFormatter.string(from: controller.event?.startDate ?? Date())
let endTime = dateFormatter.string(from: controller.event?.endDate ?? Date())
let sameEventsDay = allTimes.filter { event in
return event.from?.date == startDate
}
var availableTimes = sameEventsDay.map { event -> CalendarModels.availabelTimes in
let eventFormatter = DateFormatter()
eventFormatter.dateFormat = "HH:mm:ss"
let from = dateFormatter.string(from: eventFormatter.date(from: event.from?.time ?? "") ?? Date())
let to = dateFormatter.string(from: eventFormatter.date(from: event.to?.time ?? "") ?? Date())
return CalendarModels.availabelTimes(date: event.from?.date, from: from, to: to)
}
view.showActivityView()
if action.rawValue == 1 {
availableTimes.append(CalendarModels.availabelTimes(date: startDate, from: startTime, to: endTime))
}
interactor?.setAvailableTimes(availableTime: availableTimes)
controller.dismiss(animated: true, completion: nil)
}
func displayAvailableTimes(times: [[MainNetworkModels.AvailableTimes]]){
view.hideActivityView()
allTimes.removeAll()
times.forEach { item in
allTimes.append(contentsOf: item)
}
events.removeAll()
reloadData()
}
func displayError(errorMessage: String) {
view.hideActivityView()
showAlert(errorMessage: errorMessage, completion: nil)
}
func showAlert(errorMessage: String, completion: (( _ choose: Bool) -> Void)?){
Alert(title: "error", message: errorMessage)
.addAction("ok", style: .default) { _ in
completion?(true)
}
.show()
}
func displayUnAuthorized(){
SharedPrefrences.shared.deconnectUSer()
router?.goToLogin()
}
override func dayView(dayView: DayView, willMoveTo date: Date){
let calendarDate = Calendar.current.dateComponents([.year, .month], from: date)
if (calendarDate.month != currentmonth) {
events.removeAll()
allTimes.removeAll()
view.showActivityView()
interactor?.getAvailableTimes(month: String(calendarDate.month ?? 1), year: String(calendarDate.year ?? 2022))
currentmonth = calendarDate.month
}
}
}
`
When I was compiling this project, it said "Cannot find 'ekEvent' in scope"
The error happened in the line 74 of the "CalenderViewController". How can I fix the problems about the event kit. Please help me out. Thank you very much.
In IOS 17, a new API is used for calendar Event store access. Code needs to add in logic to detect that in CalendarViewController. The new API call is eventStore.requestFullAccessToEvents. You will also need to add in a new reference in the plist for full access. Privacy - Calendar Full Access Usage Description
private func requestAccessToCalendar() {
do {
if #available(iOS 17.0, *) {
eventStore.requestFullAccessToEvents { [weak self] granted, error in
DispatchQueue.main.async {
guard let self else { return }
self.initializeStore()
self.subscribeToNotifications()
self.reloadData()
}
}
} else {
eventStore.requestAccess(to: .event) { [weak self] granted, error in
// Handle the response to the request.
DispatchQueue.main.async {
guard let self else { return }
self.initializeStore()
self.subscribeToNotifications()
self.reloadData()
}
}
}
}
}
Hi @richardtop I found a bug (maybe not) when I add invitee from ios calendar and go to calenderkit view and click that event app directly crashes or when I click add invitee in the app in calender kit view it directly crashes. Is there a way to solve it or a way to remove that section that says add invitee? Even I remove the add invitee in the calender kit view if a user has added an invitee from ios calender it will keep crushing. do you have a solution for that? Any document or walktrough video smth?
`
import UIKit
import CalendarKit
import EventKit
import EventKitUI
import Firebase
import FirebaseAuth
class BsCalendarViewController: DayViewController, EKEventEditViewDelegate {
private let eventStore = EKEventStore()
let db = Firestore.firestore()
let currentUserMail = Auth.auth().currentUser?.email
var xx = ""
override func viewDidLoad() {
super.viewDidLoad()
requestAccessToCalendar()
subscribeToNotifications()
getCurrentUserData()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: self.uploadCurrentUserCalendar)
let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
appearance.shadowColor = nil
let navigationBar = navigationController!.navigationBar
navigationBar.standardAppearance = appearance
navigationBar.scrollEdgeAppearance = appearance
self.navigationController?.setToolbarHidden(true, animated: false)
}
func getCurrentUserData() {
db.collection("xx").document(xx!).getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data()
guard let type = dataDescription?["x x"] else {return}
self.xx = type as! String
} else {
print("Document does not exist")
}
}
}
var eventBox: [EventBox] = []
func uploadCurrentUserCalendar() {
self.db.collection(xx).document(xx!).collection("Calendar").getDocuments() { (querySnapshot, err) in
if let err = err {
let alert = UIAlertController(title: "Upload Error", message: err.localizedDescription, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
return
} else {
for document in querySnapshot!.documents {
document.reference.delete()
}
let startDate = Calendar.current.date(byAdding: .day, value: -7, to: Date())
var oneDayComponents = DateComponents()
oneDayComponents.day = 35
let endDate = self.calendar.date(byAdding: oneDayComponents, to: startDate!)!
let predicate = self.eventStore.predicateForEvents(withStart: startDate!, end: endDate, calendars: nil)
let eventKitEvents = self.eventStore.events(matching: predicate)
for event in eventKitEvents {
if let startDate = event.startDate,
let endDate = event.endDate,
let isAllDay = event.isAllDay as? Bool,
let title = event.title{
let newEventBox = EventBox(startDate: startDate, endDate: endDate, isAllDay: isAllDay, title: title)
self.eventBox.append(newEventBox)
}
}
for uploadData in self.eventBox {
self.db.collection(x.xx).document(x.xx!).collection("Calendar").document(uploadData.title).setData([ "title" : uploadData.title, "startDate" : uploadData.startDate, "endDate" : uploadData.endDate, "isAllDay" : uploadData.isAllDay])
}
}
}
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.setToolbarHidden(true, animated: false)
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setToolbarHidden(true, animated: false)
}
func subscribeToNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(storeChanged(_:)), name: .EKEventStoreChanged, object: nil)
}
@objc func storeChanged(_ notification: Notification) {
reloadData()
uploadCurrentUserCalendar()
}
func requestAccessToCalendar() {
eventStore.requestAccess(to: .event) { success, error in
}
}
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
let startDate = date
var oneDayComponents = DateComponents()
oneDayComponents.day = 1
let endDate = calendar.date(byAdding: oneDayComponents, to: startDate)!
let predicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: nil)
let eventKitEvents = eventStore.events(matching: predicate)
let calenderKitEvents = eventKitEvents.map(EKWrapper.init)
return calenderKitEvents
}
override func dayViewDidSelectEventView(_ eventView: EventView) {
guard let ckEvent = eventView.descriptor as? EKWrapper else {
return
}
let ekEvent = ckEvent.ekEvent
presentDetailView(ekEvent)
}
private func presentDetailView(_ ekEvent: EKEvent) {
let eventViewController = EKEventViewController()
eventViewController.event = ekEvent
eventViewController.allowsCalendarPreview = true
eventViewController.allowsEditing = true
navigationController?.pushViewController(eventViewController, animated: true)
}
override func dayViewDidLongPressEventView(_ eventView: EventView) {
endEventEditing()
guard let ckEvent = eventView.descriptor as? EKWrapper else { return }
beginEditing(event: ckEvent, 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 {
// event creation flow
presentEditingViewForEvent(editingEvent.ekEvent)
} else {
// editing flow
try! eventStore.save(editingEvent.ekEvent, span: .thisEvent)
}
}
reloadData()
}
func presentEditingViewForEvent(_ ekEvent: EKEvent) {
let editingViewController = EKEventEditViewController()
editingViewController.editViewDelegate = self
editingViewController.event = ekEvent
editingViewController.eventStore = eventStore
present(editingViewController, animated: true, completion: nil)
}
override func dayView(dayView: DayView, didTapTimelineAt date: Date) {
endEventEditing()
}
override func dayViewDidBeginDragging(dayView: DayView) {
endEventEditing()
}
override func dayView(dayView: DayView, didLongPressTimelineAt date: Date) {
let newEKEvent = EKEvent(eventStore: eventStore)
newEKEvent.calendar = eventStore.defaultCalendarForNewEvents
var oneHourComponents = DateComponents()
oneHourComponents.hour = 1
let endDate = calendar.date(byAdding: oneHourComponents, to: date)
newEKEvent.startDate = date
newEKEvent.endDate = endDate
newEKEvent.title = "New Event"
let newEKWrapper = EKWrapper(eventKitEvent: newEKEvent)
newEKWrapper.editedEvent = newEKWrapper
create(event: newEKWrapper, animated: true)
}
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
endEventEditing()
reloadData()
controller.dismiss(animated: true, completion: nil)
}
}`
Hi, i am using the latest CK Version. When i start my app the first time, i granted the permission to access my calendar. But after that, CalendarKit is not able to display events. I try to call reloadData() in requestAccessToCalendar() but with now success.
What do i need to change to display events after request access without starting the app again?
If so, how would I go about doing that?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.