Add 'CalendarrKit/' from commit '9ea1a8705d608b1aa476c0b35685c57466bb0c50'
git-subtree-dir: CalendarrKit git-subtree-mainline:8bc32faa7fgit-subtree-split:9ea1a8705d
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import Foundation
|
||||
|
||||
/// Cross-process "the snapshot changed" broadcast.
|
||||
///
|
||||
/// Two sandboxed apps cannot see each other's `NotificationCenter`, and neither
|
||||
/// can poll the container cheaply. Darwin notifications are the one signal that
|
||||
/// crosses the process boundary without an extra entitlement, a running
|
||||
/// connection, or a shared framework.
|
||||
///
|
||||
/// The payload is always empty — Darwin notifications carry no data. The signal
|
||||
/// means only "read again". If it is ever missed, a consumer that also reloads
|
||||
/// on window activation degrades to a slightly stale view rather than a wrong one.
|
||||
public enum SnapshotChangeNotifier {
|
||||
public static let name = "com.scarriffleservices.calendarr.snapshot-changed"
|
||||
|
||||
/// Announce that the snapshot was rewritten. Called by `SnapshotStore`.
|
||||
public static func post() {
|
||||
CFNotificationCenterPostNotification(
|
||||
CFNotificationCenterGetDarwinNotifyCenter(),
|
||||
CFNotificationName(name as CFString),
|
||||
nil, nil, true)
|
||||
}
|
||||
|
||||
/// Start listening. The handler runs on an arbitrary thread, so hop to
|
||||
/// whatever actor your UI needs before touching state.
|
||||
///
|
||||
/// Returns a token you must keep alive; deinit removes the observer.
|
||||
public static func observe(_ handler: @escaping @Sendable () -> Void) -> ObservationToken {
|
||||
ObservationToken(handler: handler)
|
||||
}
|
||||
|
||||
public final class ObservationToken: @unchecked Sendable {
|
||||
private let handler: @Sendable () -> Void
|
||||
|
||||
fileprivate init(handler: @escaping @Sendable () -> Void) {
|
||||
self.handler = handler
|
||||
// Darwin notifications hand back no context pointer we can rely on
|
||||
// across process boundaries, so route through an unretained pointer
|
||||
// to this token and look it up in the callback.
|
||||
let observer = Unmanaged.passUnretained(self).toOpaque()
|
||||
CFNotificationCenterAddObserver(
|
||||
CFNotificationCenterGetDarwinNotifyCenter(),
|
||||
observer,
|
||||
{ _, observer, _, _, _ in
|
||||
guard let observer else { return }
|
||||
Unmanaged<ObservationToken>.fromOpaque(observer)
|
||||
.takeUnretainedValue().handler()
|
||||
},
|
||||
SnapshotChangeNotifier.name as CFString,
|
||||
nil,
|
||||
.deliverImmediately)
|
||||
}
|
||||
|
||||
deinit {
|
||||
CFNotificationCenterRemoveObserver(
|
||||
CFNotificationCenterGetDarwinNotifyCenter(),
|
||||
Unmanaged.passUnretained(self).toOpaque(),
|
||||
CFNotificationName(SnapshotChangeNotifier.name as CFString),
|
||||
nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user