In order to use old Result-based completion hanlders, please use tag 0.6.0.
Getting Started
First thing first:
Add Calendar usage description to your app’s Info.plist to request for user’s Calendars access.
<key>NSCalendarsUsageDescription</key>
<string>"$(PRODUCT_NAME) needs your permission to create events"</string>
(Optional) configure own calendar name to request access to, preferrable in AppDelegate‘s didFinishLaunchingWithOptions (Swift) or App‘s init() (SwiftUI):
Swift AppDelegate:
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Shift.configureWithAppName("MyApp")
return true
}
}
in SwiftUI App, first import Shift, then configure your app’s name to differntiate the name of your app’s calendar in system’s EventKit.
import SwiftUI
import Shift
@main
struct MyApp: App {
init() {
Shift.configureWithAppName("MyApp")
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
A quick NOTE about concurrency
Since async functions can only be called on concurrency context, if you call async function inside synchornouse context, Xcode will throws an error:
So, there are two ways to awaiting for concurrency result, base on context:
Inside async function
func doSomethingAsync() async {
// ... other works
let events = try? await Shift.shared.fetchEvents(for: Date())
// ... other works
}
Inside Task closure:
func regularFunction() {
// ... other works
Task {
let events = try? await Shift.shared.fetchEvents(for: Date())
// then...
}
// ... other works
}
func fetchEvents() async {
do {
let events = try await Shift.shared.fetchEvents(for: Date()) // await for events fetching
} catch {
print(error) // handle error
}
}
or standalone:
Task {
let events = try? await Shift.shared.fetchEvents(for: Date()) // await for events fetching
}
Result-based completion handlers (old pattern, but still doable if you preferred this to async/await)
Shift.shared.fetchEvents(for: Date()) { result in
switch result {
case let .success(events): print(events) // got events
case let .failure(error): print(error) // handle error
}
}
Shift.shared.fetchEventsRangeUntilEndOfDay(from: Date()) { result in
switch result {
case let .success(events): print(events) // got events
case let .failure(error): print(error) // handle error
}
}
Shift.shared.createEvent("Be happy!", startDate: startTime, endDate: endTime) { result in
switch result {
case let .success(event): print(event) // created event
case let .failure(error): print(error) // handle error
}
}
Shift.shared.deleteEvent(identifier: eventID) { result in
switch result {
case let .success: print("done!") // deleted event
case let .failure(error): print(error) // handle error
}
}
SwiftUI Example
Shift is conformed ObservableObject with an @Publishedevents property, so it’s straight-forward to use in SwiftUI binding mechanism.
Result-based example:
import EventKit
import SwiftUI
import Shift
struct ContentView: View {
@StateObject var eventKitWrapper = Shift.shared
@State private var selectedEvent: EKEvent?
var body: some View {
LazyVStack(alignment: .leading, spacing: 10) {
ForEach(eventKitWrapper.events, id: \.self) { event in
Text(event: event)
}
}
.padding()
.onAppear {
eventKitWrapper.fetchEventsForToday()
}
}
}
async/await example:
import EventKit
import SwiftUI
import Shift
struct ContentView: View {
@StateObject var eventKitWrapper = Shift.shared
@State private var selectedEvent: EKEvent?
var body: some View {
LazyVStack(alignment: .leading, spacing: 10) {
ForEach(eventKitWrapper.events, id: \.self) { event in
Text(event: event)
}
}
.padding()
.task {
try? await eventKitWrapper.fetchEventsForToday()
}
}
}
Apps currently using Shift
Clendar - Clendar - universal calendar app. Written in SwiftUI. Available on App Store. MIT License.
Shift
Light-weight EventKit wrapper.
Shift is a light-weight concurrency wrapper for EventKit:
async
/await
. (tag:0.7.0
)Result
completion handler if preferred (tag:0.6.0
)Shift is currently being used by Clendar app.
Requirement
0.7.0
0.6.0
Install
This component is built using Swift Package Manager, it is pretty straight forward to use:
0.7.0
0.6.0
Tag Version:
Concurrency support is now ready, in tag
0.7.0
In order to use old Result-based completion hanlders, please use tag
0.6.0
.Getting Started
First thing first:
AppDelegate
‘sdidFinishLaunchingWithOptions
(Swift) orApp
‘sinit()
(SwiftUI):Swift AppDelegate:
in SwiftUI App, first
import Shift
, then configure your app’s name to differntiate the name of your app’s calendar in system’s EventKit.A quick NOTE about concurrency
Since
async
functions can only be called on concurrency context, if you callasync
function inside synchornouse context, Xcode will throws an error:So, there are two ways to
await
ing for concurrency result, base on context:Inside
async
functionInside
Task
closure:Either is fine, base on caller’s context.
In SwiftUI views, you can call
async
functions inside View’.task
modifier:Usage Example
Fetch list of events for a particular date:
async/await (NEW)
inside regular async function:
or standalone:
Result-based completion handlers (old pattern, but still doable if you preferred this to async/await)
Create Event:
async/await
inside regular async function:
or standalone:
Result
Delete event:
async/await
inside regular async function:
or standalone:
Result
SwiftUI Example
Shift is conformed
ObservableObject
with an@Published
events
property, so it’s straight-forward to use in SwiftUI binding mechanism.Result-based example:
async/await example:
Apps currently using Shift
(add yours here)
Help, feedback or suggestions?
Feel free to open an issue or contact me on Twitter for discussions, news & announcements & other projects. 🚀
I hope you like it! :)