import ActivityKit import BackgroundTasks import SwiftUI @main struct FuelBoardApp: App { init() { WatchSyncManager.shared.activate() #if DEBUG // QA hook (Debug builds only): `-qaLiveActivity e10|e5|diesel` starts a // Live Activity with a long station name so the Lock Screen / island // layout can be rendered in the Simulator for visual QA. let args = ProcessInfo.processInfo.arguments if let idx = args.firstIndex(of: "-qaLiveActivity"), args.indices.contains(idx + 1), let fuel = FuelType(rawValue: args[idx + 1]) { startQALiveActivity(fuel: fuel) } #endif } var body: some Scene { WindowGroup { ContentView() .onOpenURL(perform: handleOpenURL) .onReceive(NotificationCenter.default.publisher(for: .fuelBoardWatchRefreshRequested)) { _ in WatchSyncManager.shared.pushSnapshot() } } .backgroundTask(.appRefresh(SmartDataRefreshScheduler.taskIdentifier)) { await SmartDataRefreshCoordinator.runBackgroundProbe() } } #if DEBUG private func startQALiveActivity(fuel: FuelType) { let state = FuelBoardLiveActivityAttributes.ContentState( fuel: fuel, stationID: "qa-phoenix", stationName: "Phoenix Filling Stations", brand: "Phoenix", pricePence: 1499, priceDisplayStyle: FuelStore.loadPriceDisplayStyle(), distanceKM: 8.0, lat: 51.5, lng: -0.12, updatedAt: Date() ) let attrs = FuelBoardLiveActivityAttributes() do { let activity = try Activity.request( attributes: attrs, content: .init(state: state, staleDate: nil), pushType: nil ) print("QA-LIVE-ACTIVITY STARTED id=\(activity.id)") } catch { print("QA-LIVE-ACTIVITY FAILED: \(error)") } } #endif /// Handles deep links that end up in the app. Widget taps arrive here in /// two cases: /// - legacy/cached widget timelines using the `fuelboard://` relay, or /// - the Home Screen delivering a `maps://` URL to the containing app /// instead of opening Maps directly (the system's choice on iOS). /// Either way we forward to the native Apple Maps directions URL. The /// `http://maps.apple.com` form is also handled, from very old timelines. /// In CarPlay the widget uses the same `maps://` URL, which routes to /// Apple Maps without ever launching this app. /// A `fuelboard://station?stationID=…&lat=…&lng=…&name=…` link (from a /// widget or Live Activity tap when Settings → Tap station = More info) /// posts a notification that ContentView observes to present the detail /// sheet (falling back to the map sheet when the station has left /// the cache). private func handleOpenURL(_ url: URL) { guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return } if components.scheme == "fuelboard", components.host == "station" { let items = components.queryItems ?? [] var userInfo: [String: Any] = [:] if let id = items.first(where: { $0.name == "stationID" })?.value { userInfo["stationID"] = id } if let lat = items.first(where: { $0.name == "lat" })?.value.flatMap(Double.init) { userInfo["stationLat"] = lat } if let lng = items.first(where: { $0.name == "lng" })?.value.flatMap(Double.init) { userInfo["stationLng"] = lng } if let name = items.first(where: { $0.name == "name" })?.value { userInfo["stationName"] = name } NotificationCenter.default.post( name: .fuelBoardShowStationDetail, object: nil, userInfo: userInfo ) return } guard let items = components.queryItems else { return } // Preferred: maps://?daddr=lat,lng&t=d (also covers http://maps.apple.com). if let daddr = items.first(where: { $0.name == "daddr" })?.value { openMaps(daddr: daddr) return } // Legacy relay: fuelboard://directions?lat=..&lng=.. if let lat = items.first(where: { $0.name == "lat" })?.value.flatMap(Double.init), let lng = items.first(where: { $0.name == "lng" })?.value.flatMap(Double.init) { openMaps(daddr: "\(lat),\(lng)") } } private func openMaps(daddr: String) { guard let mapsURL = URL(string: "maps://?daddr=\(daddr)&t=d") else { return } UIApplication.shared.open(mapsURL) } }