From 8fb0731ebb55a85fe0be11cba297a26848a2c686 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Fri, 21 Nov 2025 10:39:26 +0530 Subject: [PATCH] Lagging as Hell --- DynamicIsland/ContentView.swift | 56 +++++++++++++++-------- DynamicIsland/managers/MusicManager.swift | 49 +++++++++++++------- 2 files changed, 68 insertions(+), 37 deletions(-) diff --git a/DynamicIsland/ContentView.swift b/DynamicIsland/ContentView.swift index c49aa7a..62b8b0c 100644 --- a/DynamicIsland/ContentView.swift +++ b/DynamicIsland/ContentView.swift @@ -72,8 +72,8 @@ struct ContentView: View { @State private var gestureProgress: CGFloat = .zero @State private var isMusicControlWindowVisible = false - @State private var pendingMusicControlSync: DispatchWorkItem? - @State private var musicControlHideWorkItem: DispatchWorkItem? + @State private var pendingMusicControlTask: Task? + @State private var musicControlHideTask: Task? @State private var musicControlVisibilityDeadline: Date? @State private var haptics: Bool = false @@ -1005,7 +1005,9 @@ struct ContentView: View { if isPlaying { clearMusicControlVisibilityDeadline() - scheduleMusicControlWindowSync(forceRefresh: true) + if vm.notchState == .closed { + scheduleMusicControlWindowSync(forceRefresh: false) + } } else { extendMusicControlVisibilityAfterPause() } @@ -1027,7 +1029,9 @@ struct ContentView: View { let deadline = Date().addingTimeInterval(musicControlPauseGrace) musicControlVisibilityDeadline = deadline scheduleMusicControlVisibilityCheck(deadline: deadline) - scheduleMusicControlWindowSync(forceRefresh: true) + if vm.notchState == .closed { + scheduleMusicControlWindowSync(forceRefresh: false) + } } private func clearMusicControlVisibilityDeadline() { @@ -1038,24 +1042,31 @@ struct ContentView: View { private func scheduleMusicControlVisibilityCheck(deadline: Date) { cancelMusicControlVisibilityTimer() - let workItem = DispatchWorkItem { - musicControlHideWorkItem = nil + let interval = max(0, deadline.timeIntervalSinceNow) + + musicControlHideTask = Task { @MainActor [interval] in + defer { musicControlHideTask = nil } + + if interval > 0 { + let nanoseconds = UInt64(interval * 1_000_000_000) + try? await Task.sleep(nanoseconds: nanoseconds) + } + + guard !Task.isCancelled else { return } if let currentDeadline = musicControlVisibilityDeadline, currentDeadline <= Date() { musicControlVisibilityDeadline = nil } - scheduleMusicControlWindowSync(forceRefresh: true) + if vm.notchState == .closed { + scheduleMusicControlWindowSync(forceRefresh: false) + } } - - musicControlHideWorkItem = workItem - let interval = max(0, deadline.timeIntervalSinceNow) - DispatchQueue.main.asyncAfter(deadline: .now() + interval, execute: workItem) } private func cancelMusicControlVisibilityTimer() { - musicControlHideWorkItem?.cancel() - musicControlHideWorkItem = nil + musicControlHideTask?.cancel() + musicControlHideTask = nil } private func musicControlVisibilityIsActive() -> Bool { @@ -1089,8 +1100,16 @@ struct ContentView: View { } let syncDelay = max(0, delay) - let workItem = DispatchWorkItem { [forceRefresh] in - pendingMusicControlSync = nil + + pendingMusicControlTask = Task { @MainActor [forceRefresh, syncDelay] in + defer { pendingMusicControlTask = nil } + + if syncDelay > 0 { + let nanoseconds = UInt64(syncDelay * 1_000_000_000) + try? await Task.sleep(nanoseconds: nanoseconds) + } + + guard !Task.isCancelled else { return } if shouldShowMusicControlWindow() { syncMusicControlWindow(forceRefresh: forceRefresh) @@ -1098,15 +1117,12 @@ struct ContentView: View { hideMusicControlWindow() } } - - pendingMusicControlSync = workItem - DispatchQueue.main.asyncAfter(deadline: .now() + syncDelay, execute: workItem) #endif } private func cancelMusicControlWindowSync() { - pendingMusicControlSync?.cancel() - pendingMusicControlSync = nil + pendingMusicControlTask?.cancel() + pendingMusicControlTask = nil } #if os(macOS) diff --git a/DynamicIsland/managers/MusicManager.swift b/DynamicIsland/managers/MusicManager.swift index b5dbb3a..1419319 100644 --- a/DynamicIsland/managers/MusicManager.swift +++ b/DynamicIsland/managers/MusicManager.swift @@ -33,6 +33,7 @@ class MusicManager: ObservableObject { private var cancellables = Set() private var controllerCancellables = Set() private var debounceIdleTask: Task? + @MainActor private var pendingOptimisticPlayState: Bool? // Helper to check if macOS has removed support for NowPlayingController public private(set) var isNowPlayingDeprecated: Bool = false @@ -193,20 +194,38 @@ class MusicManager: ObservableObject { forceUpdate() } + @MainActor + private func applyPlayState(_ state: Bool, animation: Animation?) { + if let animation { + var transaction = Transaction() + transaction.animation = animation + withTransaction(transaction) { + self.isPlaying = state + } + } else { + self.isPlaying = state + } + + self.updateIdleState(state: state) + } + // MARK: - Update Methods @MainActor private func updateFromPlaybackState(_ state: PlaybackState) { // Check for playback state changes (playing/paused) - if state.isPlaying != self.isPlaying { - NSLog("Playback state changed: \(state.isPlaying ? "Playing" : "Paused")") - withAnimation(.smooth) { - self.isPlaying = state.isPlaying - self.updateIdleState(state: state.isPlaying) - } + let eventIsPlaying = state.isPlaying + let expectedState = pendingOptimisticPlayState + pendingOptimisticPlayState = nil - if state.isPlaying && !state.title.isEmpty && !state.artist.isEmpty { - self.updateSneakPeek() - } + if eventIsPlaying != self.isPlaying { + let animation: Animation? = (expectedState == eventIsPlaying) ? .smooth(duration: 0.18) : .smooth + applyPlayState(eventIsPlaying, animation: animation) + } else { + self.updateIdleState(state: eventIsPlaying) + } + + if eventIsPlaying && !state.title.isEmpty && !state.artist.isEmpty { + self.updateSneakPeek() } // Check for changes in track metadata using last artwork change values @@ -486,20 +505,16 @@ class MusicManager: ObservableObject { } func togglePlay() { - let controller = activeController + guard let controller = activeController else { return } Task { await MainActor.run { let newState = !isPlaying - withAnimation(.smooth(duration: 0.18)) { - isPlaying = newState - if newState { - isPlayerIdle = false - } - } + pendingOptimisticPlayState = newState + applyPlayState(newState, animation: .smooth(duration: 0.18)) } - await controller?.togglePlay() + await controller.togglePlay() } }