gitbay/gitbayApp.swift
72 lines · 3026 bytes
1 symbol in this file
1import SwiftUI
2
3@main
4struct gitbayApp: App {
5
6 @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
7 @State private var session: SessionStore
8 @State private var router = PushRouter()
9 @State private var registrar: PushRegistrar
10
11 init() {
12 GitbayFonts.register()
13 // The registrar takes the session at construction, so both are
14 // built here rather than as property initialisers — one
15 // registrar for the life of the app, which the delegate and
16 // SessionStore's removal hook then both point at.
17 let session = SessionStore()
18 _session = State(wrappedValue: session)
19 _registrar = State(wrappedValue: PushRegistrar(session: session))
20 }
21
22 var body: some Scene {
23 WindowGroup {
24 ContentView()
25 .environment(session)
26 .environment(router)
27 .environment(registrar)
28 .font(.gbSans(.body))
29 .tint(.gbAccent)
30 // The screenshot checkpoint forces appearance per run;
31 // simulator clones ignore the base device's setting.
32 .preferredColorScheme(Self.forcedColorScheme)
33 .onOpenURL { url in
34 // A page the app has no screen for goes back to
35 // Safari; the rest waits for ContentView's drain.
36 if WebURLRoute(url: url) == nil {
37 UIApplication.shared.open(url)
38 } else {
39 router.pendingLink = url
40 }
41 }
42 .task {
43 let registrar = self.registrar
44 appDelegate.registrar = registrar
45 appDelegate.router = router
46 appDelegate.session = session
47 // Weak: the registrar holds the session, and this
48 // closure is stored on the session.
49 session.willRemoveAccount = { [weak registrar] account in
50 await registrar?.deregister(account)
51 }
52 session.didAddAccount = { [weak registrar] account in
53 await registrar?.register(account)
54 }
55 // A tap that launched the app reaches the delegate
56 // before this runs; it parked the payload.
57 appDelegate.deliverPendingNotification()
58 await registrar.registerIfAlreadyAuthorized()
59 }
60 }
61 }
62
63 /// A clone follows whatever appearance it was created with, so the
64 /// checkpoint has to name the one it wants. Neither flag means the
65 /// app follows the system, as it does for real users.
66 private static var forcedColorScheme: ColorScheme? {
67 let args = ProcessInfo.processInfo.arguments
68 if args.contains("-gb-dark") { return .dark }
69 if args.contains("-gb-light") { return .light }
70 return nil
71 }
72}