Home > Software engineering >  How to subscribe to EventKit changes in SwiftUI?
How to subscribe to EventKit changes in SwiftUI?

Time:02-14

I am trying to understand how to subscribe to EventKit calendar event updates within SwiftIU.

The EventKit documentation shows how to do this in Swift (https://developer.apple.com/documentation/eventkit/updating_with_notifications).

It says to subscribe to calendar update notifications by writing:

NotificationCenter.default.addObserver(self, selector: Selector("storeChanged:"), name: .EKEventStoreChanged, object: eventStore)

It also appears that the storeChanged function needs do be tagged with @objc based on xcode warnings. However, when I do the below within a SwiftUI app, I get the error @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

import SwiftUI
import EventKit

struct ContentView: View {
    let eventStore = EKEventStore()
    
    var body: some View {
        Text("Hello, world!")
            .padding()
            .onAppear{
                NotificationCenter.default.addObserver(self, selector: Selector("observeEvents"), name: .EKEventStoreChanged, object: eventStore)
            }
    }
    
    @objc func observeEvents() {
        print("called with updated events")
    }
}

Am I doing something wrong? What would be the way to subscribe to calendar event updates within the EventStore in SwiftUI?

CodePudding user response:

You have a couple of options. One is to use an ObservableObject that can have @objc functions and declare it as the delegate for the notifications:

struct ContentView: View {
    @StateObject private var eventDelegate = EventDelegate()
    
    var body: some View {
        Text("Hello, world!")
            .padding()
    }
}

class EventDelegate: ObservableObject {
    let eventStore = EKEventStore()
    
    init() {
        NotificationCenter.default.addObserver(self, selector: #selector(observeEvents), name: .EKEventStoreChanged, object: eventStore)
        //remember to clean up your observers later
    }
    
    @objc func observeEvents() {
        print("called with updated events")
    }
}

A second option would be to use NotificationCenter's publisher inside your View. Note this example is incomplete, as it's unclear what actions you want to take with the notification, but it's a template to get you started:

struct ContentView: View {
    @State private var eventStore = EKEventStore() //normally wouldn't use @State for a reference type like this, but it seems pointless to recreate it
    @State private var pub : NotificationCenter.Publisher?
    
    var body: some View {
        Text("Hello, world!")
            .padding()
            .onAppear {
                pub = NotificationCenter.default.publisher(for: .EKEventStoreChanged, object: eventStore)
                //use sink or assign here
            }
    }
}
  • Related