TalerWallet1App.swift (4513B)
1 /* 2 * This file is part of GNU Taler, ©2022-26 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * Main app entry point 7 * 8 * @author Jonathan Buchanan 9 * @author Marc Stibane 10 */ 11 import BackgroundTasks 12 import SwiftUI 13 import os.log 14 import SymLog 15 16 @main 17 struct TalerWallet1App: App { 18 #if TALER_NIGHTLY 19 @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 20 #endif 21 private let symLog = SymLogV() 22 @Environment(\.scenePhase) private var phase 23 @StateObject private var viewState = ViewState.shared // popToRootView() 24 @StateObject private var viewState2 = ViewState2.shared // popToRootView() 25 @State private var soundPlayed = false 26 27 private let walletCore = WalletCore.shared 28 private let controller = Controller.shared 29 private let model = WalletModel.shared 30 private let debugViewC = DebugViewC.shared 31 let logger = Logger(subsystem: "net.taler.gnu", category: "Main App") 32 private let biometricService = BiometricService.shared 33 34 func scheduleAppRefresh() { 35 let request = BGAppRefreshTaskRequest(identifier: "net.taler.refresh") 36 request.earliestBeginDate = .now.addingTimeInterval(4 * 3600) 37 try? BGTaskScheduler.shared.submit(request) 38 } 39 40 var body: some Scene { 41 let mainView = MainView(logger: logger, stack: CallStack("App"), soundPlayed: $soundPlayed) 42 .environmentObject(debugViewC) // change viewID / sheetID 43 .environmentObject(viewState) // popToRoot 44 .environmentObject(viewState2) // popToRoot 45 .environmentObject(controller) 46 .environmentObject(model) 47 .environmentObject(biometricService) 48 .addKeyboardVisibilityToEnvironment() 49 /// external events are taler:// or payto:// URLs passed to this app 50 /// we handle them in .onOpenURL in MainView.swift 51 .handlesExternalEvents(preferring: ["*"], allowing: ["*"]) 52 .task { 53 #if DEBUG 54 let testing = true 55 #else 56 let testing = false 57 #endif 58 try! await controller.initWalletCore(model, setTesting: testing) // will (and should) crash on failure 59 } 60 if #available(iOS 16.4, *) { 61 return WindowGroup { 62 mainView 63 } 64 .onChange(of: phase) { newPhase in 65 switch newPhase { 66 case .active: 67 logger.log("❗️.onChange() ==> Active") 68 case .background: 69 logger.log("❗️.onChange() ==> Background)") 70 // scheduleAppRefresh() 71 default: break 72 } 73 } 74 .backgroundTask(.appRefresh("net.taler.refresh")) { 75 // symLog.log("backgroundTask running") 76 //#if 0 77 // let request = URLRequest(url: URL(string: "your_backend")!) 78 // guard let data = try? await URLSession.shared.data(for: request).0 else { 79 // return 80 // } 81 // 82 // let decoder = JSONDecoder() 83 // guard let products = try? decoder.decode([Product].self, from: data) else { 84 // return 85 // } 86 // 87 // if !products.isEmpty && !Task.isCancelled { 88 // await notifyUser(for: products) 89 // } 90 //#endif 91 } 92 } else { 93 // Fallback on earlier versions 94 return WindowGroup { 95 mainView 96 } 97 } 98 99 } 100 } 101 102 final class ViewState : ObservableObject { 103 static let shared = ViewState() 104 @Published var rootViewId = UUID() 105 let logger = Logger(subsystem: "net.taler.gnu", category: "ViewState") 106 107 public func popToRootView(_ stack: CallStack?) -> Void { 108 logger.info("popToRootView") 109 rootViewId = UUID() // setting a new ID will cause 1st NavStack popToRootView behaviour 110 } 111 112 private init() { } 113 } 114 115 final class ViewState2 : ObservableObject { 116 static let shared = ViewState2() 117 @Published var rootViewId = UUID() 118 let logger = Logger(subsystem: "net.taler.gnu", category: "ViewState2") 119 120 public func popToRootView(_ stack: CallStack?) -> Void { 121 logger.info("popToRootView") 122 rootViewId = UUID() // setting a new ID will cause 2nd NavStack popToRootView behaviour 123 } 124 125 private init() { } 126 }