66// Copyright © 2015 Nathan Racklyeft. All rights reserved.
77//
88
9- import HealthKit
10- import Intents
11- import LoopCore
12- import LoopKit
13- import LoopKitUI
149import UIKit
15- import UserNotifications
10+ import LoopKit
1611
1712@UIApplicationMain
18- final class AppDelegate : UIResponder , UIApplicationDelegate , DeviceOrientationController {
19-
20- private lazy var log = DiagnosticLog ( category: " AppDelegate " )
21-
22- private lazy var pluginManager = PluginManager ( )
23-
24- private var alertManager : AlertManager !
25- private var deviceDataManager : DeviceDataManager !
26- private var loopAlertsManager : LoopAlertsManager !
27- private var bluetoothStateManager : BluetoothStateManager !
28- private var trustedTimeChecker : TrustedTimeChecker !
29-
13+ final class AppDelegate : UIResponder , UIApplicationDelegate {
3014 var window : UIWindow ?
31-
32- var launchOptions : [ UIApplication . LaunchOptionsKey : Any ] ?
33-
34- private var rootViewController : RootNavigationController ! {
35- return window? . rootViewController as? RootNavigationController
36- }
37-
38- private func isAfterFirstUnlock( ) -> Bool {
39- let fileManager = FileManager . default
40- do {
41- let documentDirectory = try fileManager. url ( for: . documentDirectory, in: . userDomainMask, appropriateFor: nil , create: false )
42- let fileURL = documentDirectory. appendingPathComponent ( " protection.test " )
43- guard fileManager. fileExists ( atPath: fileURL. path) else {
44- let contents = Data ( " unimportant " . utf8)
45- try ? contents. write ( to: fileURL, options: . completeFileProtectionUntilFirstUserAuthentication)
46- // If file doesn't exist, we're at first start, which will be user directed.
47- return true
48- }
49- let contents = try ? Data ( contentsOf: fileURL)
50- return contents != nil
51- } catch {
52- log. error ( " Could not create after first unlock test file: %@ " , String ( describing: error) )
53- }
54- return false
55- }
56-
57- private func finishLaunch( application: UIApplication ) {
58- log. default ( " Finishing launching " )
59- UIDevice . current. isBatteryMonitoringEnabled = true
60-
61- bluetoothStateManager = BluetoothStateManager ( )
62- alertManager = AlertManager ( rootViewController: rootViewController, expireAfter: Bundle . main. localCacheDuration)
63- deviceDataManager = DeviceDataManager ( pluginManager: pluginManager, alertManager: alertManager, bluetoothStateManager: bluetoothStateManager, rootViewController: rootViewController)
64-
65- let statusTableViewController = UIStoryboard ( name: " Main " , bundle: Bundle ( for: AppDelegate . self) ) . instantiateViewController ( withIdentifier: " MainStatusViewController " ) as! StatusTableViewController
66-
67- statusTableViewController. deviceManager = deviceDataManager
68-
69- bluetoothStateManager. addBluetoothStateObserver ( statusTableViewController)
70-
71- loopAlertsManager = LoopAlertsManager ( alertManager: alertManager, bluetoothStateManager: bluetoothStateManager)
72-
73- SharedLogging . instance = deviceDataManager. loggingServicesManager
74-
75- deviceDataManager? . analyticsServicesManager. application ( application, didFinishLaunchingWithOptions: launchOptions)
7615
77- OrientationLock . deviceOrientationController = self
16+ private let loopAppManager = LoopAppManager ( )
17+ private let log = DiagnosticLog ( category: " AppDelegate " )
7818
79- NotificationManager . authorize ( delegate: self )
80-
81- rootViewController. pushViewController ( statusTableViewController, animated: false )
82-
83- let notificationOption = launchOptions ? [ . remoteNotification]
84-
85- if let notification = notificationOption as? [ String : AnyObject ] {
86- deviceDataManager? . handleRemoteNotification ( notification)
87- }
88-
89- scheduleBackgroundTasks ( )
90-
91- launchOptions = nil
92-
93- trustedTimeChecker = TrustedTimeChecker ( alertManager)
94- }
19+ // MARK: - UIApplicationDelegate - Initialization
9520
9621 func application( _ application: UIApplication , didFinishLaunchingWithOptions launchOptions: [ UIApplication . LaunchOptionsKey : Any ] ? ) -> Bool {
97-
98- self . launchOptions = launchOptions
99-
100- log. default ( " didFinishLaunchingWithOptions %{public}@ " , String ( describing: launchOptions) )
101-
102- registerBackgroundTasks ( )
22+ log. default ( " %{public}@ with launchOptions: %{public}@ " , #function, String ( describing: launchOptions) )
10323
104- guard isAfterFirstUnlock ( ) else {
105- log . default ( " Launching before first unlock; pausing launch... " )
106- return false
107- }
24+ loopAppManager . initialize ( with : launchOptions )
25+ loopAppManager . launch ( into : window )
26+ return loopAppManager . isLaunchComplete
27+ }
10828
109- finishLaunch ( application : application )
29+ // MARK: - UIApplicationDelegate - Life Cycle
11030
111- window? . tintColor = . loopAccent
31+ func applicationDidBecomeActive( _ application: UIApplication ) {
32+ log. default ( #function)
11233
113- return true
34+ loopAppManager . didBecomeActive ( )
11435 }
11536
11637 func applicationWillResignActive( _ application: UIApplication ) {
@@ -125,139 +46,47 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, DeviceOrientationCo
12546 log. default ( #function)
12647 }
12748
128- func applicationDidBecomeActive( _ application: UIApplication ) {
129- deviceDataManager? . updatePumpManagerBLEHeartbeatPreference ( )
130- }
131-
13249 func applicationWillTerminate( _ application: UIApplication ) {
13350 log. default ( #function)
13451 }
13552
136- // MARK: - Continuity
137-
138- func application( _ application: UIApplication , continue userActivity: NSUserActivity , restorationHandler: @escaping ( [ UIUserActivityRestoring ] ? ) -> Void ) -> Bool {
139- log. default ( #function)
140-
141- if #available( iOS 12 . 0 , * ) {
142- if userActivity. activityType == NewCarbEntryIntent . className {
143- log. default ( " Restoring %{public}@ intent " , userActivity. activityType)
144- rootViewController. restoreUserActivityState ( . forNewCarbEntry( ) )
145- return true
146- }
147- }
148-
149- switch userActivity. activityType {
150- case NSUserActivity . newCarbEntryActivityType,
151- NSUserActivity . viewLoopStatusActivityType:
152- log. default ( " Restoring %{public}@ activity " , userActivity. activityType)
153- restorationHandler ( [ rootViewController] )
154- return true
155- default :
156- return false
157- }
158- }
159-
160- // MARK: - Remote notifications
161- func application( _ application: UIApplication , didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) {
162- let tokenParts = deviceToken. map { data in String ( format: " %02.2hhx " , data) }
163- let token = tokenParts. joined ( )
164- log. default ( " RemoteNotifications device token: %{public}@ " , token)
165- deviceDataManager? . loopManager. settings. deviceToken = deviceToken
166- }
53+ // MARK: - UIApplicationDelegate - Environment
16754
168- func application( _ application: UIApplication , didFailToRegisterForRemoteNotificationsWithError error: Error ) {
169- log. error ( " Failed to register: %{public}@ " , String ( describing: error) )
170- }
171-
172- func application( _ application: UIApplication ,
173- didReceiveRemoteNotification userInfo: [ AnyHashable : Any ] ,
174- fetchCompletionHandler completionHandler: @escaping ( UIBackgroundFetchResult ) -> Void ) {
175- guard let notification = userInfo as? [ String : AnyObject ] else {
176- completionHandler ( . failed)
177- return
178- }
179-
180- deviceDataManager? . handleRemoteNotification ( notification)
181- completionHandler ( . noData)
182- }
183-
18455 func applicationProtectedDataDidBecomeAvailable( _ application: UIApplication ) {
185- log. default ( " applicationProtectedDataDidBecomeAvailable " )
186-
187- if deviceDataManager == nil {
188- finishLaunch ( application: application)
56+ if !loopAppManager. isLaunchComplete {
57+ loopAppManager. launch ( into: window)
18958 }
19059 }
191-
192- // MARK: - DeviceOrientationController
19360
194- var supportedInterfaceOrientations = UIInterfaceOrientationMask . allButUpsideDown
61+ // MARK: - UIApplicationDelegate - Remote Notification
19562
196- func setOriginallySupportedInferfaceOrientations( ) {
197- supportedInterfaceOrientations = UIInterfaceOrientationMask . allButUpsideDown
198- }
63+ func application( _ application: UIApplication , didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) {
64+ log. default ( #function)
19965
200- func application( _ application: UIApplication , supportedInterfaceOrientationsFor window: UIWindow ? ) -> UIInterfaceOrientationMask {
201- supportedInterfaceOrientations
66+ loopAppManager. setRemoteNotificationsDeviceToken ( deviceToken)
20267 }
20368
204- // MARK: - Background Tasks
205-
206- private func registerBackgroundTasks( ) {
207- if DeviceDataManager . registerCriticalEventLogHistoricalExportBackgroundTask ( { self . deviceDataManager. handleCriticalEventLogHistoricalExportBackgroundTask ( $0) } ) {
208- log. debug ( " Critical event log export background task registered " )
209- } else {
210- log. error ( " Critical event log export background task not registered " )
211- }
69+ func application( _ application: UIApplication , didFailToRegisterForRemoteNotificationsWithError error: Error ) {
70+ log. error ( " %{public}@ with error: %{public}@ " , #function, String ( describing: error) )
21271 }
21372
214- private func scheduleBackgroundTasks( ) {
215- deviceDataManager. scheduleCriticalEventLogHistoricalExportBackgroundTask ( )
216- }
217- }
73+ func application( _ application: UIApplication , didReceiveRemoteNotification userInfo: [ AnyHashable : Any ] , fetchCompletionHandler completionHandler: @escaping ( UIBackgroundFetchResult ) -> Void ) {
74+ log. default ( #function)
21875
219- // MARK: UNUserNotificationCenterDelegate implementation
76+ completionHandler ( loopAppManager. handleRemoteNotification ( userInfo as? [ String : AnyObject ] ) ? . noData : . failed)
77+ }
22078
221- extension AppDelegate : UNUserNotificationCenterDelegate {
222- func userNotificationCenter( _ center: UNUserNotificationCenter , didReceive response: UNNotificationResponse , withCompletionHandler completionHandler: @escaping ( ) -> Void ) {
223- switch response. actionIdentifier {
224- case NotificationManager . Action. retryBolus. rawValue:
225- if let units = response. notification. request. content. userInfo [ LoopNotificationUserInfoKey . bolusAmount. rawValue] as? Double ,
226- let startDate = response. notification. request. content. userInfo [ LoopNotificationUserInfoKey . bolusStartDate. rawValue] as? Date ,
227- startDate. timeIntervalSinceNow >= TimeInterval ( minutes: - 5 )
228- {
229- deviceDataManager? . analyticsServicesManager. didRetryBolus ( )
79+ // MARK: - UIApplicationDelegate - Continuity
23080
231- deviceDataManager? . enactBolus ( units: units, at: startDate) { ( _) in
232- completionHandler ( )
233- }
234- return
235- }
236- case NotificationManager . Action. acknowledgeAlert. rawValue:
237- let userInfo = response. notification. request. content. userInfo
238- if let alertIdentifier = userInfo [ LoopNotificationUserInfoKey . alertTypeID. rawValue] as? Alert . AlertIdentifier ,
239- let managerIdentifier = userInfo [ LoopNotificationUserInfoKey . managerIDForAlert. rawValue] as? String {
240- alertManager. acknowledgeAlert ( identifier:
241- Alert . Identifier ( managerIdentifier: managerIdentifier, alertIdentifier: alertIdentifier) )
242- }
243- default :
244- break
245- }
81+ func application( _ application: UIApplication , continue userActivity: NSUserActivity , restorationHandler: @escaping ( [ UIUserActivityRestoring ] ? ) -> Void ) -> Bool {
82+ log. default ( #function)
24683
247- completionHandler ( )
84+ return loopAppManager . userActivity ( userActivity , restorationHandler : restorationHandler )
24885 }
24986
250- func userNotificationCenter( _ center: UNUserNotificationCenter , willPresent notification: UNNotification , withCompletionHandler completionHandler: @escaping ( UNNotificationPresentationOptions ) -> Void ) {
251- switch notification. request. identifier {
252- // TODO: Until these notifications are converted to use the new alert system, they shall still show in the foreground
253- case LoopNotificationCategory . bolusFailure. rawValue,
254- LoopNotificationCategory . pumpBatteryLow. rawValue,
255- LoopNotificationCategory . pumpExpired. rawValue,
256- LoopNotificationCategory . pumpFault. rawValue:
257- completionHandler ( [ . badge, . sound, . alert] )
258- default :
259- // All other userNotifications are not to be displayed while in the foreground
260- completionHandler ( [ ] )
261- }
87+ // MARK: - UIApplicationDelegate - Interface
88+
89+ func application( _ application: UIApplication , supportedInterfaceOrientationsFor window: UIWindow ? ) -> UIInterfaceOrientationMask {
90+ return loopAppManager. supportedInterfaceOrientations
26291 }
26392}
0 commit comments