NavidromeApp/watchOS/App/NavidromeWatchApp.swift
2026-04-11 16:15:27 -07:00

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 {
try? 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()
}
}
}