diff --git a/Loop/AppDelegate.swift b/Loop/AppDelegate.swift index df2430f732..401ae1c467 100644 --- a/Loop/AppDelegate.swift +++ b/Loop/AppDelegate.swift @@ -18,49 +18,92 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - private(set) lazy var deviceManager = DeviceDataManager() + private var deviceManager: DeviceDataManager? private var rootViewController: RootNavigationController! { return window?.rootViewController as? RootNavigationController } - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + private var isAfterFirstUnlock: Bool { + let fileManager = FileManager.default + do { + let documentDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false) + let fileURL = documentDirectory.appendingPathComponent("protection.test") + guard fileManager.fileExists(atPath: fileURL.path) else { + let contents = Data("unimportant".utf8) + try? contents.write(to: fileURL, options: .completeFileProtectionUntilFirstUserAuthentication) + // If file doesn't exist, we're at first start, which will be user directed. + return true + } + let contents = try? Data(contentsOf: fileURL) + return contents != nil + } catch { + log.error(error) + } + return false + } + + private func finishLaunch() { + log.default("Finishing launching") + + deviceManager = DeviceDataManager() + NotificationManager.authorize(delegate: self) + + let mainStatusViewControllers = UIStoryboard(name: "Main", bundle: Bundle(for: AppDelegate.self)).instantiateViewController(withIdentifier: "MainStatusViewController") as! StatusTableViewController - log.info(#function) - - AnalyticsManager.shared.application(application, didFinishLaunchingWithOptions: launchOptions) + rootViewController.pushViewController(mainStatusViewControllers, animated: false) rootViewController.rootViewController.deviceManager = deviceManager + } + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + log.default("didFinishLaunchingWithOptions \(String(describing: launchOptions))") + + AnalyticsManager.shared.application(application, didFinishLaunchingWithOptions: launchOptions) + + guard isAfterFirstUnlock else { + log.default("Launching before first unlock; pausing launch...") + return false + } + + finishLaunch() + let notificationOption = launchOptions?[.remoteNotification] if let notification = notificationOption as? [String: AnyObject] { - deviceManager.handleRemoteNotification(notification) + deviceManager?.handleRemoteNotification(notification) } return true } func applicationWillResignActive(_ application: UIApplication) { + log.default(#function) } func applicationDidEnterBackground(_ application: UIApplication) { + log.default(#function) } func applicationWillEnterForeground(_ application: UIApplication) { + log.default(#function) } func applicationDidBecomeActive(_ application: UIApplication) { - deviceManager.updatePumpManagerBLEHeartbeatPreference() + deviceManager?.updatePumpManagerBLEHeartbeatPreference() } func applicationWillTerminate(_ application: UIApplication) { + log.default(#function) } // MARK: - Continuity func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + log.default(#function) if #available(iOS 12.0, *) { if userActivity.activityType == NewCarbEntryIntent.className { @@ -86,7 +129,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) } let token = tokenParts.joined() log.default("RemoteNotifications device token: \(token)") - deviceManager.loopManager.settings.deviceToken = deviceToken + deviceManager?.loopManager.settings.deviceToken = deviceToken } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { @@ -102,9 +145,17 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { return } - deviceManager.handleRemoteNotification(notification) + deviceManager?.handleRemoteNotification(notification) completionHandler(.noData) } + + func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) { + log.default("applicationProtectedDataDidBecomeAvailable") + + if deviceManager == nil { + finishLaunch() + } + } } @@ -119,7 +170,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { { AnalyticsManager.shared.didRetryBolus() - deviceManager.enactBolus(units: units, at: startDate) { (_) in + deviceManager?.enactBolus(units: units, at: startDate) { (_) in completionHandler() } return diff --git a/Loop/Base.lproj/Main.storyboard b/Loop/Base.lproj/Main.storyboard index 364c95188e..b59845e58e 100644 --- a/Loop/Base.lproj/Main.storyboard +++ b/Loop/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -21,7 +21,7 @@ - + @@ -70,7 +70,7 @@ @@ -113,7 +113,7 @@ Future glucose is predicted by combining the effects of multiple inputs. Use this tool to toggle various inputs to see how they compare to the final prediction. - + @@ -270,7 +270,7 @@ - + @@ -368,7 +368,7 @@ @@ -401,7 +401,7 @@ @@ -462,7 +462,7 @@ @@ -541,7 +541,7 @@ - + @@ -775,16 +775,16 @@ - + @@ -1028,7 +1025,7 @@ An insulin activity model is used to estimate effects of insulin on glucose levels. An accurate model can help prevent insulin stacking and safely recommend corrective treatments. - + @@ -1067,7 +1064,6 @@ - @@ -1130,8 +1126,8 @@ - - + +