diff --git a/Sources/PlexBar/App/PlexBarApp.swift b/Sources/PlexBar/App/PlexBarApp.swift index 1d34152..4097cb5 100644 --- a/Sources/PlexBar/App/PlexBarApp.swift +++ b/Sources/PlexBar/App/PlexBarApp.swift @@ -18,6 +18,7 @@ struct PlexBarApp: App { @State private var historyStore: PlexHistoryStore @State private var libraryStore: PlexLibraryStore @State private var serverPreviewStore: PlexServerPreviewStore + private let systemLifecycleObserver: PlexSystemLifecycleObserver private let updateService: PlexUpdateService init() { @@ -52,6 +53,9 @@ struct PlexBarApp: App { libraryStore: libraryStore, client: runtime.authClient )) + systemLifecycleObserver = PlexSystemLifecycleObserver { + sessionStore.refreshNow() + } updateService = PlexUpdateService() } diff --git a/Sources/PlexBar/Support/PlexSystemLifecycleObserver.swift b/Sources/PlexBar/Support/PlexSystemLifecycleObserver.swift new file mode 100644 index 0000000..ca22f1d --- /dev/null +++ b/Sources/PlexBar/Support/PlexSystemLifecycleObserver.swift @@ -0,0 +1,26 @@ +import AppKit + +final class PlexSystemLifecycleObserver { + private let notificationCenter: NotificationCenter + private let didWakeObserver: NSObjectProtocol + + init( + notificationCenter: NotificationCenter = NSWorkspace.shared.notificationCenter, + onDidWake: @escaping @MainActor () -> Void + ) { + self.notificationCenter = notificationCenter + didWakeObserver = notificationCenter.addObserver( + forName: NSWorkspace.didWakeNotification, + object: nil, + queue: .main + ) { _ in + Task { @MainActor in + onDidWake() + } + } + } + + deinit { + notificationCenter.removeObserver(didWakeObserver) + } +} diff --git a/Tests/PlexBarTests/PlexSystemLifecycleObserverTests.swift b/Tests/PlexBarTests/PlexSystemLifecycleObserverTests.swift new file mode 100644 index 0000000..d5c2a37 --- /dev/null +++ b/Tests/PlexBarTests/PlexSystemLifecycleObserverTests.swift @@ -0,0 +1,56 @@ +import AppKit +import Foundation +import Testing +@testable import PlexBar + +@MainActor +@Test func systemWakeNotificationRunsWakeHandler() async throws { + let notificationCenter = NotificationCenter() + let counter = LifecycleObserverCounter() + do { + let observer = PlexSystemLifecycleObserver( + notificationCenter: notificationCenter + ) { + counter.increment() + } + + notificationCenter.post(name: NSWorkspace.didWakeNotification, object: nil) + + await waitForLifecycleObserver { + counter.value == 1 + } + + #expect(counter.value == 1) + withExtendedLifetime(observer) {} + } + + notificationCenter.post(name: NSWorkspace.didWakeNotification, object: nil) + try await Task.sleep(for: .milliseconds(50)) + + #expect(counter.value == 1) +} + +@MainActor +private func waitForLifecycleObserver( + timeoutNanoseconds: UInt64 = 2_000_000_000, + condition: @escaping @MainActor () -> Bool +) async { + let deadline = DispatchTime.now().uptimeNanoseconds + timeoutNanoseconds + + while DispatchTime.now().uptimeNanoseconds < deadline { + if condition() { + return + } + + try? await Task.sleep(nanoseconds: 10_000_000) + } +} + +@MainActor +private final class LifecycleObserverCounter { + private(set) var value = 0 + + func increment() { + value += 1 + } +}