krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

App/Sources/MoonriseApp.swift

04b2b1d914788057bcd20945b876847386eb8f65
moonrise/App/Sources/MoonriseApp.swift history · blame · raw

68 lines · 3147 bytes

7 symbols in this file
 1import SkyRenderer
 2import SwiftUI
 3import UIKit
 4import UserNotifications
 5import os
 6
 7let log = Logger(subsystem: "sh.krz.moonrise", category: "app")
 8
 9@main
10struct MoonriseApp: App {
11    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
12    @AppStorage(SettingsKey.onboarded) private var onboarded = false
13    @AppStorage(SettingsKey.nightActive) private var nightActive = false
14    @State private var router = Router.shared
15
16    var body: some Scene {
17        WindowGroup {
18            TabView(selection: $router.tab) {
19                Tab("Sky", systemImage: "sparkles", value: "sky") { SkyScreen() }
20                Tab("System", systemImage: "circle.circle", value: "system") { SystemScreen() }
21                Tab("Sun", systemImage: "sun.max", value: "sun") { SunScreen() }
22                Tab("News & launches", systemImage: "airplane.departure", value: "launches") { LaunchesScreen() }
23                Tab("Library", systemImage: "books.vertical", value: "library") { LibraryScreen() }
24            }
25            .tabViewStyle(.sidebarAdaptable)
26            // Red tab highlight while the Sky tab is in night mode.
27            .tint(router.tab == "sky" && nightActive ? SkyStyle.nightRed : nil)
28            .preferredColorScheme(.dark)
29            .environment(\.openURL, InAppBrowser.action)
30            .fullScreenCover(isPresented: .constant(!onboarded)) {
31                OnboardingScreen().preferredColorScheme(.dark)
32            }
33        }
34    }
35}
36
37final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
38    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
39        ImageLoader.configureURLCache()
40        UNUserNotificationCenter.current().delegate = self
41        application.registerForRemoteNotifications()
42        return true
43    }
44
45    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
46        let token = deviceToken.map { String(format: "%02x", $0) }.joined()
47        log.info("APNs device token: \(token, privacy: .public)")
48        Task { @MainActor in AuroraAlerts.shared.receivedToken(token) }
49    }
50
51    /// A news digest opens News & launches on the News switch.
52    nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
53        guard response.notification.request.content.userInfo["tab"] as? String == "news" else { return }
54        await MainActor.run {
55            Router.shared.tab = "launches"
56            Router.shared.showNews = true
57        }
58    }
59
60    /// Show alerts as banners while the app is open.
61    nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
62        [.banner, .sound]
63    }
64
65    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
66        log.error("APNs registration failed: \(error.localizedDescription, privacy: .public)")
67    }
68}