From fb24807905ec5ca40b762f2cd9feb423c9df3202 Mon Sep 17 00:00:00 2001 From: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com> Date: Fri, 2 Sep 2022 14:14:20 +0000 Subject: [PATCH] Rewrite `AppDelegate#checkIfAppIsReady` in Swift --- Signal/src/AppDelegate.h | 6 +++ Signal/src/AppDelegate.m | 99 +++--------------------------------- Signal/src/AppDelegate.swift | 71 ++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 93 deletions(-) diff --git a/Signal/src/AppDelegate.h b/Signal/src/AppDelegate.h index 620acf6044..35f9659b39 100644 --- a/Signal/src/AppDelegate.h +++ b/Signal/src/AppDelegate.h @@ -14,4 +14,10 @@ extern NSString *const kAppLaunchesAttemptedKey; @interface AppDelegate : UIResponder +@property (nonatomic, readonly) NSTimeInterval launchStartedAt; + +@property (nonatomic, readonly) BOOL areVersionMigrationsComplete; + +@property (nonatomic, readonly) BOOL didAppLaunchFail; + @end diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index a9672ca2ff..51ac176c01 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -53,8 +53,6 @@ static NSString *const kURLHostAddStickersPrefix = @"addstickers"; NSString *const kURLHostTransferPrefix = @"transfer"; NSString *const kURLHostLinkDevicePrefix = @"linkdevice"; -static NSTimeInterval launchStartedAt; - static void uncaughtExceptionHandler(NSException *exception) { if (SSKDebugFlags.internalLogging) { @@ -90,8 +88,11 @@ static void uncaughtExceptionHandler(NSException *exception) @interface AppDelegate () -@property (nonatomic) BOOL areVersionMigrationsComplete; -@property (nonatomic) BOOL didAppLaunchFail; +@property (nonatomic, readwrite) NSTimeInterval launchStartedAt; + +@property (nonatomic, readwrite) BOOL areVersionMigrationsComplete; + +@property (nonatomic, readwrite) BOOL didAppLaunchFail; @property (nonatomic) BOOL shouldKillAppWhenBackgrounded; @end @@ -160,7 +161,7 @@ static void uncaughtExceptionHandler(NSException *exception) // This should be the first thing we do. SetCurrentAppContext([MainAppContext new]); - launchStartedAt = CACurrentMediaTime(); + self.launchStartedAt = CACurrentMediaTime(); [BenchManager startEventWithTitle:@"Presenting HomeView" eventId:@"AppStart" logInProduction:TRUE]; BOOL isLoggingEnabled; @@ -1157,94 +1158,6 @@ static void uncaughtExceptionHandler(NSException *exception) [self checkIfAppIsReady]; } -- (void)checkIfAppIsReady -{ - OWSAssertIsOnMainThread(); - - // If launch failed, the app will never be ready. - if (self.didAppLaunchFail) { - return; - } - - // App isn't ready until storage is ready AND all version migrations are complete. - if (!self.areVersionMigrationsComplete) { - return; - } - if (![self.storageCoordinator isStorageReady]) { - return; - } - if ([AppReadiness isAppReady]) { - // Only mark the app as ready once. - return; - } - BOOL launchJobsAreComplete = [self.launchJobs ensureLaunchJobsWithCompletion:^{ - // If launch jobs need to run, return and - // call checkIfAppIsReady again when they're complete. - [self checkIfAppIsReady]; - }]; - if (!launchJobsAreComplete) { - // Wait for launch jobs to complete. - return; - } - - OWSLogInfo(@"checkIfAppIsReady"); - - // Note that this does much more than set a flag; - // it will also run all deferred blocks. - [AppReadiness setAppIsReadyUIStillPending]; - - if (CurrentAppContext().isRunningTests) { - OWSLogVerbose(@"Skipping post-launch logic in tests."); - [AppReadiness setUIIsReady]; - return; - } - - // If user is missing profile name, redirect to onboarding flow. - if (!SSKEnvironment.shared.profileManager.hasProfileName) { - DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) { - [self.tsAccountManager setIsOnboarded:NO transaction:transaction]; - }); - } - - if ([self.tsAccountManager isRegistered]) { - [self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) { - NSUInteger deviceCount = [OWSDevice anyCountWithTransaction:transaction]; - NSString *linkedDeviceMessage; - if (deviceCount > 1) { - linkedDeviceMessage = [NSString stringWithFormat:@"%lu devices including the primary", deviceCount]; - } else { - linkedDeviceMessage = @"no linked devices"; - } - OWSLogInfo(@"localAddress: %@, deviceId: %u (%@)", - [self.tsAccountManager localAddressWithTransaction:transaction], - [self.tsAccountManager storedDeviceIdWithTransaction:transaction], - linkedDeviceMessage); - }]; - - // This should happen at any launch, background or foreground. - [OWSSyncPushTokensJob run]; - } - - [DebugLogger.shared postLaunchLogCleanup]; - [AppVersion.shared mainAppLaunchDidComplete]; - - if (!Environment.shared.preferences.hasGeneratedThumbnails) { - [self.databaseStorage - asyncReadWithBlock:^(SDSAnyReadTransaction *transaction) { - [TSAttachment anyEnumerateWithTransaction:transaction - batched:YES - block:^(TSAttachment *attachment, BOOL *stop) { - // no-op. It's sufficient to initWithCoder: each object. - }]; - } - completion:^{ - [Environment.shared.preferences setHasGeneratedThumbnails:YES]; - }]; - } - - [SignalApp.shared ensureRootViewController:launchStartedAt]; -} - - (void)registrationStateDidChange { OWSAssertIsOnMainThread(); diff --git a/Signal/src/AppDelegate.swift b/Signal/src/AppDelegate.swift index 701afe946f..7c15c4adee 100644 --- a/Signal/src/AppDelegate.swift +++ b/Signal/src/AppDelegate.swift @@ -152,4 +152,75 @@ extension AppDelegate { } } } + + @objc + func checkIfAppIsReady() { + AssertIsOnMainThread() + + // If launch failed, the app will never be ready. + guard !didAppLaunchFail else { return } + + // App isn't ready until storage is ready AND all version migrations are complete. + guard areVersionMigrationsComplete else { return } + guard storageCoordinator.isStorageReady else { return } + + // Only mark the app as ready once. + guard !AppReadiness.isAppReady else { return } + + // If launch jobs need to run, return and call checkIfAppIsReady again when they're complete. + let launchJobsAreComplete = launchJobs.ensureLaunchJobs { + self.checkIfAppIsReady() + } + guard launchJobsAreComplete else { return } + + Logger.info("checkIfAppIsReady") + + // Note that this does much more than set a flag; + // it will also run all deferred blocks. + AppReadiness.setAppIsReadyUIStillPending() + + guard !CurrentAppContext().isRunningTests else { + Logger.verbose("Skipping post-launch logic in tests.") + AppReadiness.setUIIsReady() + return + } + + // If user is missing profile name, redirect to onboarding flow. + if !SSKEnvironment.shared.profileManager.hasProfileName { + databaseStorage.write { transaction in + self.tsAccountManager.setIsOnboarded(false, transaction: transaction) + } + } + + if tsAccountManager.isRegistered { + databaseStorage.read { transaction in + let localAddress = self.tsAccountManager.localAddress(with: transaction) + let deviceId = self.tsAccountManager.storedDeviceId(with: transaction) + let deviceCount = OWSDevice.anyCount(transaction: transaction) + let linkedDeviceMessage = deviceCount > 1 ? "\(deviceCount) devices including the primary" : "no linked devices" + Logger.info("localAddress: \(String(describing: localAddress)), deviceId: \(deviceId) (\(linkedDeviceMessage))") + } + + // This should happen at any launch, background or foreground. + SyncPushTokensJob.run() + } + + DebugLogger.shared().postLaunchLogCleanup() + AppVersion.shared().mainAppLaunchDidComplete() + + if !Environment.shared.preferences.hasGeneratedThumbnails() { + databaseStorage.asyncRead( + block: { transaction in + TSAttachment.anyEnumerate(transaction: transaction, batched: true) { (_, _) in + // no-op. It's sufficient to initWithCoder: each object. + } + }, + completion: { + Environment.shared.preferences.setHasGeneratedThumbnails(true) + } + ) + } + + SignalApp.shared().ensureRootViewController(launchStartedAt) + } }