NowPlayingView.swift — The _radioProgressBarImpl stub I left as a “reference” comment block was still compiled by Swift and referenced isDraggingSlider, dragPosition, and playbackTime which no longer exist on NowPlayingView. Removed the entire stub. Also transportControls had one remaining playbackTime reference — replaced with audioPlayer.currentTime directly since transport controls genuinely need the current position for the timeshift enable/disable logic and don’t cause a body re-eval issue there. NavidromeWatchApp.swift — syncLibrary() is declared async not async throws, so try? was redundant. Removed it.
74 lines
2.9 KiB
Swift
74 lines
2.9 KiB
Swift
import SwiftUI
|
|
import WatchConnectivity
|
|
import WatchKit
|
|
|
|
@main
|
|
struct NavidromeWatchApp: App {
|
|
|
|
@WKApplicationDelegateAdaptor(NavidromeWatchDelegate.self) var appDelegate
|
|
|
|
@StateObject private var watchManager = WatchSessionManager.shared
|
|
@StateObject private var audioPlayer = WatchAudioPlayer.shared
|
|
@StateObject private var offlineStore = WatchOfflineStore.shared
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
WatchRootView()
|
|
.environmentObject(watchManager)
|
|
.environmentObject(audioPlayer)
|
|
.environmentObject(offlineStore)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Watch App Delegate
|
|
// Required to handle WKURLSessionRefreshBackgroundTask — without this, the
|
|
// background URLSession in WatchOfflineStore never fires its completion delegate
|
|
// when downloads finish while the app is suspended. sessionSendsLaunchEvents=true
|
|
// in WatchOfflineStore has no effect without this handler (AUDIT-053).
|
|
class NavidromeWatchDelegate: NSObject, WKApplicationDelegate {
|
|
|
|
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
|
|
for task in backgroundTasks {
|
|
switch task {
|
|
case let urlSessionTask as WKURLSessionRefreshBackgroundTask:
|
|
// Re-connect the background URLSession by identifier so the system
|
|
// can hand back the completed download events to WatchOfflineStore's
|
|
// URLSessionDownloadDelegate. Merely accessing the backgroundSession
|
|
// lazy property is enough — it registers the delegate and the system
|
|
// delivers pending delegate calls immediately after.
|
|
let identifier = urlSessionTask.sessionIdentifier
|
|
if identifier == "com.navidromeplayer.watch.download" {
|
|
_ = WatchOfflineStore.shared.backgroundSession
|
|
print("[Watch] WKURLSessionRefreshBackgroundTask handled: \(identifier)")
|
|
}
|
|
urlSessionTask.setTaskCompletedWithSnapshot(false)
|
|
|
|
case let refreshTask as WKApplicationRefreshBackgroundTask:
|
|
// Opportunity for a lightweight library metadata refresh.
|
|
Task {
|
|
if WatchSessionManager.shared.activeServer != nil {
|
|
await WatchSessionManager.shared.syncLibrary()
|
|
}
|
|
refreshTask.setTaskCompletedWithSnapshot(false)
|
|
}
|
|
|
|
default:
|
|
task.setTaskCompletedWithSnapshot(false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct WatchRootView: View {
|
|
@EnvironmentObject var watchManager: WatchSessionManager
|
|
@EnvironmentObject var offlineStore: WatchOfflineStore
|
|
|
|
var body: some View {
|
|
if offlineStore.songs.isEmpty && watchManager.servers.isEmpty {
|
|
WatchSetupView()
|
|
} else {
|
|
WatchLibraryView()
|
|
}
|
|
}
|
|
}
|