From 1268a436aea11d998289c41266059929a68d61e6 Mon Sep 17 00:00:00 2001 From: Tommaso Negri Date: Fri, 7 Aug 2026 23:51:31 +0200 Subject: make Notifier more generic and improve docs --- Sources/phbar/Models/Notifier.swift | 71 +++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 18 deletions(-) (limited to 'Sources') diff --git a/Sources/phbar/Models/Notifier.swift b/Sources/phbar/Models/Notifier.swift index 1124cd4..a428c49 100644 --- a/Sources/phbar/Models/Notifier.swift +++ b/Sources/phbar/Models/Notifier.swift @@ -1,32 +1,64 @@ import Foundation -// Darwin notifications via CoreFoundation — the macOS-native, signal-like way -// to trigger a refresh from the shell with no client binary: -// -// notifyutil -p -// -// e.g. notifyutil -p house.panini.phbar.touch.workspaces -// -// (notify.h isn't part of Swift's Darwin module, so we go through -// CFNotificationCenter, which talks to the same subsystem.) -// -// The callback fires on an arbitrary thread; handlers must hop to the main -// actor themselves (see AppDelegate.handleRefresh). - /// Retains the handler for the lifetime of the registration. +/// +/// The raw pointer returned by ``Notifier/register(_:handler:)`` +/// is a retained reference to this box; it is released when +/// the registration is cancelled. private final class NotifyBox { let handler: @Sendable () -> Void init(_ handler: @escaping @Sendable () -> Void) { self.handler = handler } } +/// Delivers refresh triggers via Darwin notifications. +/// +/// Wraps CoreFoundation notifications — the macOS-native, +/// signal-like way to trigger behaviour from the shell with +/// no client binary, using `notifyutil`: +/// +/// ``` +/// notifyutil -p +/// ``` +/// +/// Each category of events uses a different ``prefix`` +/// to avoid collisions. For example, block refresh +/// notifications use ``touchPrefix``: +/// +/// ``` +/// notifyutil -p house.panini.phbar.touch. +/// ``` +/// +/// > Note: `notify.h` isn't part of Swift's Darwin module, +/// > so we go through `CFNotificationCenter`, which talks +/// > to the same subsystem. +/// > +/// > The callback fires on an arbitrary thread; handlers +/// > must hop to the main actor themselves (see ``AppDelegate``). enum Notifier { - static let prefix = "house.panini.phbar.touch." + /// Prefix for block refresh notifications. + /// + /// Use `notifyutil -p house.panini.phbar.touch.` to trigger + /// a block update from the shell. + static let touchPrefix = "house.panini.phbar.touch." /// Register `handler` to fire whenever the notification is posted. + /// + /// - Parameters: + /// - name: The notification name suffix. + /// - prefix: The full notification name will be `prefix + name`. + /// Defaults to ``touchPrefix``. + /// - handler: Closure to invoke when the notification fires. + /// + /// The returned token is a retained pointer used to keep the handler + /// alive and to cancel the registration later. The handler runs on an + /// arbitrary thread, so hop to the main actor inside it if you touch + /// UI or published state. @discardableResult - static func register(_ name: String, handler: @escaping @Sendable () -> Void) - -> UnsafeMutableRawPointer - { + static func register( + _ name: String, + prefix: String = Notifier.touchPrefix, + handler: @escaping @Sendable () -> Void + ) -> UnsafeMutableRawPointer { let fullName = prefix + name let ctx = Unmanaged.passRetained(NotifyBox(handler)).toOpaque() @@ -46,7 +78,10 @@ enum Notifier { return ctx } - /// Remove and release a registration. (Optional: process exit clears all.) + /// Remove and release a registration. + /// + /// Pass the token returned by ``Notifier/register(_:handler:)``. + /// Optional: process exit clears all pending registrations anyway. static func cancel(_ token: UnsafeMutableRawPointer) { let center = CFNotificationCenterGetDarwinNotifyCenter() CFNotificationCenterRemoveEveryObserver(center, token) -- cgit v1.2.3