import Foundation /// Hand-wired singletons, shared by the phone UI, CarPlay and Siri. @MainActor final class AppGraph { static let shared = AppGraph() let settings: Settings let net: Connectivity /// Library calls: kept on disk and served from there when the server is unreachable. let apiHttp: Http /// Same disk cache, but away from home it reads only from disk instead of waiting on LAN timeouts. let carHttp: Http /// Media ranges; MediaCache is the cache. Long timeout covers server-side Opus transcodes. let mediaSession: URLSession let plex: PlexClient let reach: HomeReach let carPlex: PlexClient let stems: StemServer let cache: MediaCache let eq: EqController let playback: PlaybackManager let stemPlaylists: StemPlaylists let downloads: Downloads let coverage: StemCoverage let nowPlaying: NowPlaying let car: CarLibrary private init() { let fm = FileManager.default let support = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] let caches = fm.urls(for: .cachesDirectory, in: .userDomainMask)[0] try? fm.createDirectory(at: support, withIntermediateDirectories: true) settings = Settings() net = Connectivity() apiHttp = Http(connectTimeout: 6, readTimeout: 30, offlineCache: caches.appendingPathComponent("http")) carHttp = Http(connectTimeout: 6, readTimeout: 30, offlineCache: caches.appendingPathComponent("http")) let m = URLSessionConfiguration.default m.timeoutIntervalForRequest = 90 m.timeoutIntervalForResource = 24 * 3600 m.urlCache = nil m.requestCachePolicy = .reloadIgnoringLocalCacheData mediaSession = URLSession(configuration: m) plex = PlexClient(http: apiHttp, settings: settings) reach = HomeReach(settings: settings, net: net) let reach = reach carHttp.forceCache = { !reach.lastKnown } carPlex = PlexClient(http: carHttp, settings: settings) stems = StemServer(http: apiHttp, settings: settings, dir: support) // Media lives outside Caches' purge-anytime rules and out of iCloud backups. var media = support.appendingPathComponent("media", isDirectory: true) try? fm.createDirectory(at: media, withIntermediateDirectories: true) var rv = URLResourceValues() rv.isExcludedFromBackup = true try? media.setResourceValues(rv) let settings = settings cache = MediaCache(dir: media, session: mediaSession) { Int64(settings.cacheLimitGb) << 30 } eq = EqController(dir: support) stemPlaylists = StemPlaylists(dir: support) playback = PlaybackManager(settings: settings, plex: plex, stems: stems, cache: cache, eq: eq, dir: support) downloads = Downloads(settings: settings, plex: plex, stems: stems, cache: cache, net: net, dir: support) coverage = StemCoverage(plex: plex, stems: stems, settings: settings, dir: support) nowPlaying = NowPlaying(pm: playback, plex: plex) car = CarLibrary(plex: carPlex, reach: reach, downloads: downloads, stems: stems, pm: playback) } }