diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 1cd4fd867..101e09036 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -60,6 +60,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; @property (nonatomic) UIWindow *screenProtectionWindow; @property (nonatomic) BOOL hasInitialRootViewController; +@property (nonatomic) BOOL areVersionMigrationsComplete; @end @@ -167,7 +168,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; // performUpdateCheck must be invoked after Environment has been initialized because // upgrade process may depend on Environment. - [VersionMigrations performUpdateCheck]; + [VersionMigrations performUpdateCheckWithCompletion:^{ + OWSAssertIsOnMainThread(); + + [self versionMigrationsDidComplete]; + }]; // Accept push notification when app is not open NSDictionary *remoteNotif = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; @@ -819,11 +824,43 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; completionHandler:completionHandler]; } +- (void)versionMigrationsDidComplete +{ + OWSAssertIsOnMainThread(); + + DDLogInfo(@"%@ versionMigrationsDidComplete", self.logTag); + + self.areVersionMigrationsComplete = YES; + + [self checkIfAppIsReady]; +} + - (void)storageIsReady { OWSAssertIsOnMainThread(); DDLogInfo(@"%@ storageIsReady", self.logTag); + [self checkIfAppIsReady]; +} + +- (void)checkIfAppIsReady +{ + OWSAssertIsOnMainThread(); + + // App isn't ready until storage is ready AND all version migrations are complete. + if (!self.areVersionMigrationsComplete) { + return; + } + if (![OWSStorage isStorageReady]) { + return; + } + if ([AppReadiness isAppReady]) { + // Only mark the app as ready once. + return; + } + + DDLogInfo(@"%@ checkIfAppIsReady", self.logTag); + [OWSPreferences setIsRegistered:[TSAccountManager isRegistered]]; // Note that this does much more than set a flag; diff --git a/SignalMessaging/environment/VersionMigrations.h b/SignalMessaging/environment/VersionMigrations.h index 6eb0ccdf8..7f6e8fcb0 100644 --- a/SignalMessaging/environment/VersionMigrations.h +++ b/SignalMessaging/environment/VersionMigrations.h @@ -2,11 +2,15 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // +NS_ASSUME_NONNULL_BEGIN + #define RECENT_CALLS_DEFAULT_KEY @"RPRecentCallsDefaultKey" +typedef void (^VersionMigrationCompletion)(void); + @interface VersionMigrations : NSObject -+ (void)performUpdateCheck; ++ (void)performUpdateCheckWithCompletion:(VersionMigrationCompletion)completion; + (BOOL)isVersion:(NSString *)thisVersionString atLeast:(NSString *)openLowerBoundVersionString @@ -17,3 +21,5 @@ + (BOOL)isVersion:(NSString *)thisVersionString lessThan:(NSString *)thatVersionString; @end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/environment/VersionMigrations.m b/SignalMessaging/environment/VersionMigrations.m index 199628858..47279e55d 100644 --- a/SignalMessaging/environment/VersionMigrations.m +++ b/SignalMessaging/environment/VersionMigrations.m @@ -14,6 +14,8 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + #define NEEDS_TO_REGISTER_PUSH_KEY @"Register For Push" #define NEEDS_TO_REGISTER_ATTRIBUTES @"Register Attributes" @@ -28,11 +30,12 @@ #pragma mark Utility methods -+ (void)performUpdateCheck ++ (void)performUpdateCheckWithCompletion:(VersionMigrationCompletion)completion { // performUpdateCheck must be invoked after Environment has been initialized because // upgrade process may depend on Environment. OWSAssert([Environment current]); + OWSAssert(completion); NSString *previousVersion = AppVersion.instance.lastAppVersion; NSString *currentVersion = AppVersion.instance.currentAppVersion; @@ -47,6 +50,9 @@ OWSDatabaseMigrationRunner *runner = [[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]]; [runner assumeAllExistingMigrationsRun]; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(); + }); return; } @@ -79,7 +85,8 @@ [self clearBloomFilterCache]; } - [[[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]] runAllOutstanding]; + [[[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]] + runAllOutstandingWithCompletion:completion]; } + (BOOL)isVersion:(NSString *)thisVersionString @@ -189,3 +196,5 @@ } @end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/environment/migrations/OWS100RemoveTSRecipientsMigration.m b/SignalMessaging/environment/migrations/OWS100RemoveTSRecipientsMigration.m index 9198ebeb4..385a6c1e3 100644 --- a/SignalMessaging/environment/migrations/OWS100RemoveTSRecipientsMigration.m +++ b/SignalMessaging/environment/migrations/OWS100RemoveTSRecipientsMigration.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWS100RemoveTSRecipientsMigration.h" @@ -19,6 +19,8 @@ static NSString *const OWS100RemoveTSRecipientsMigrationId = @"100"; - (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { + OWSAssert(transaction); + NSUInteger legacyRecipientCount = [transaction numberOfKeysInCollection:@"TSRecipient"]; DDLogWarn(@"Removing %lu objects from TSRecipient collection", (unsigned long)legacyRecipientCount); [transaction removeAllObjectsInCollection:@"TSRecipient"]; diff --git a/SignalMessaging/environment/migrations/OWS101ExistingUsersBlockOnIdentityChange.m b/SignalMessaging/environment/migrations/OWS101ExistingUsersBlockOnIdentityChange.m index d8c4b4fbf..656ae23f2 100644 --- a/SignalMessaging/environment/migrations/OWS101ExistingUsersBlockOnIdentityChange.m +++ b/SignalMessaging/environment/migrations/OWS101ExistingUsersBlockOnIdentityChange.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWS101ExistingUsersBlockOnIdentityChange.h" @@ -24,6 +24,8 @@ static NSString *const OWS101ExistingUsersBlockOnIdentityChangeMigrationId = @"1 - (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { + OWSAssert(transaction); + OWSFail(@"[OWS101ExistingUsersBlockOnIdentityChange] has been obviated."); } diff --git a/SignalMessaging/environment/migrations/OWS102MoveLoggingPreferenceToUserDefaults.m b/SignalMessaging/environment/migrations/OWS102MoveLoggingPreferenceToUserDefaults.m index 9252d904d..fd77eff24 100644 --- a/SignalMessaging/environment/migrations/OWS102MoveLoggingPreferenceToUserDefaults.m +++ b/SignalMessaging/environment/migrations/OWS102MoveLoggingPreferenceToUserDefaults.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWS102MoveLoggingPreferenceToUserDefaults.h" @@ -20,6 +20,8 @@ static NSString *const OWS102MoveLoggingPreferenceToUserDefaultsMigrationId = @" - (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { + OWSAssert(transaction); + DDLogWarn(@"[OWS102MoveLoggingPreferenceToUserDefaultsMigrationId] copying existing logging preference to " @"NSUserDefaults"); diff --git a/SignalMessaging/environment/migrations/OWS103EnableVideoCalling.m b/SignalMessaging/environment/migrations/OWS103EnableVideoCalling.m index 7dd9c0b81..1fea29f4e 100644 --- a/SignalMessaging/environment/migrations/OWS103EnableVideoCalling.m +++ b/SignalMessaging/environment/migrations/OWS103EnableVideoCalling.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWS103EnableVideoCalling.h" @@ -18,8 +18,10 @@ static NSString *const OWS103EnableVideoCallingMigrationId = @"103"; } // Override parent migration -- (void)runUp +- (void)runUpWithCompletion:(OWSDatabaseMigrationCompletion)completion { + OWSAssert(completion); + DDLogWarn(@"%@ running migration...", self.logTag); if ([TSAccountManager isRegistered]) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @@ -28,18 +30,24 @@ static NSString *const OWS103EnableVideoCallingMigrationId = @"103"; success:^(NSURLSessionDataTask *task, id responseObject) { DDLogInfo(@"%@ successfully ran", self.logTag); [self save]; + + completion(); } failure:^(NSURLSessionDataTask *task, NSError *error) { if (!IsNSErrorNetworkFailure(error)) { OWSProdError([OWSAnalyticsEvents errorEnableVideoCallingRequestFailed]); } DDLogError(@"%@ failed with error: %@", self.logTag, error); + + completion(); }]; }); } else { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ DDLogInfo(@"%@ skipping; not registered", self.logTag); [self save]; + + completion(); }); } } diff --git a/SignalMessaging/environment/migrations/OWS104CreateRecipientIdentities.m b/SignalMessaging/environment/migrations/OWS104CreateRecipientIdentities.m index 4b63f5fe5..486f0b858 100644 --- a/SignalMessaging/environment/migrations/OWS104CreateRecipientIdentities.m +++ b/SignalMessaging/environment/migrations/OWS104CreateRecipientIdentities.m @@ -26,6 +26,8 @@ static NSString *const OWS104CreateRecipientIdentitiesMigrationId = @"104"; - (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { + OWSAssert(transaction); + NSMutableDictionary *identityKeys = [NSMutableDictionary new]; [transaction diff --git a/SignalMessaging/environment/migrations/OWS106EnsureProfileComplete.swift b/SignalMessaging/environment/migrations/OWS106EnsureProfileComplete.swift index fb0497b31..384c4791b 100644 --- a/SignalMessaging/environment/migrations/OWS106EnsureProfileComplete.swift +++ b/SignalMessaging/environment/migrations/OWS106EnsureProfileComplete.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // import Foundation @@ -20,7 +20,7 @@ public class OWS106EnsureProfileComplete: OWSDatabaseMigration { // Overriding runUp since we have some specific completion criteria which // is more likely to fail since it involves network requests. - override public func runUp() { + override public func runUp(completion:@escaping ((Void)) -> Void) { guard type(of: self).sharedCompleteRegistrationFixerJob == nil else { owsFail("\(self.TAG) should only be called once.") return @@ -29,6 +29,8 @@ public class OWS106EnsureProfileComplete: OWSDatabaseMigration { let job = CompleteRegistrationFixerJob(completionHandler: { Logger.info("\(self.TAG) Completed. Saving.") self.save() + + completion() }) type(of: self).sharedCompleteRegistrationFixerJob = job diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigration.h b/SignalMessaging/environment/migrations/OWSDatabaseMigration.h index 22e5835f1..487d1f427 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigration.h +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigration.h @@ -1,11 +1,13 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN +typedef void (^OWSDatabaseMigrationCompletion)(void); + @class TSStorageManager; @interface OWSDatabaseMigration : TSYapDatabaseObject @@ -18,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN // Blocking migrations running too long will crash the app, effectively bricking install // because the user will never get past it. // If you must write a launch-blocking migration, override runUp. -- (void)runUp; +- (void)runUpWithCompletion:(OWSDatabaseMigrationCompletion)completion; @end diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigration.m b/SignalMessaging/environment/migrations/OWSDatabaseMigration.m index 25c95be47..a40a25158 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigration.m +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigration.m @@ -46,8 +46,10 @@ NS_ASSUME_NONNULL_BEGIN OWSRaiseException(NSInternalInconsistencyException, @"Must override %@ in subclass", NSStringFromSelector(_cmd)); } -- (void)runUp +- (void)runUpWithCompletion:(OWSDatabaseMigrationCompletion)completion { + OWSAssert(completion); + [self.storageManager.newDatabaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { [self runUpWithTransaction:transaction]; @@ -55,6 +57,8 @@ NS_ASSUME_NONNULL_BEGIN completionBlock:^{ DDLogInfo(@"Completed migration %@", self.uniqueId); [self save]; + + completion(); }]; } diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.h b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.h index 42ea9fa36..7338b5a61 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.h +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.h @@ -4,6 +4,8 @@ NS_ASSUME_NONNULL_BEGIN +typedef void (^OWSDatabaseMigrationCompletion)(void); + @class TSStorageManager; @interface OWSDatabaseMigrationRunner : NSObject @@ -15,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Run any outstanding version migrations. */ -- (void)runAllOutstanding; +- (void)runAllOutstandingWithCompletion:(OWSDatabaseMigrationCompletion)completion; /** * On new installations, no need to migrate anything. diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m index 451eacf08..117eba4c1 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m @@ -50,21 +50,54 @@ NS_ASSUME_NONNULL_BEGIN } } -- (void)runAllOutstanding +- (void)runAllOutstandingWithCompletion:(OWSDatabaseMigrationCompletion)completion { - [self runMigrations:self.allMigrations]; + [self runMigrations:self.allMigrations completion:completion]; } - (void)runMigrations:(NSArray *)migrations + completion:(OWSDatabaseMigrationCompletion)completion { OWSAssert(migrations); + OWSAssert(completion); + NSMutableArray *migrationsToRun = [NSMutableArray new]; for (OWSDatabaseMigration *migration in migrations) { if ([OWSDatabaseMigration fetchObjectWithUniqueID:migration.uniqueId]) { DDLogDebug(@"%@ Skipping previously run migration: %@", self.logTag, migration); } else { DDLogWarn(@"%@ Running migration: %@", self.logTag, migration); - [migration runUp]; + [migrationsToRun addObject:migration]; + } + } + + if (migrationsToRun.count < 1) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(); + }); + return; + } + + NSUInteger totalMigrationCount = migrationsToRun.count; + __block NSUInteger completedMigrationCount = 0; + void (^checkMigrationCompletion)(void) = ^{ + @synchronized(self) + { + completedMigrationCount++; + if (completedMigrationCount == totalMigrationCount) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(); + }); + } + } + }; + + for (OWSDatabaseMigration *migration in migrationsToRun) { + if ([OWSDatabaseMigration fetchObjectWithUniqueID:migration.uniqueId]) { + DDLogDebug(@"%@ Skipping previously run migration: %@", self.logTag, migration); + } else { + DDLogWarn(@"%@ Running migration: %@", self.logTag, migration); + [migration runUpWithCompletion:checkMigrationCompletion]; } } } diff --git a/SignalServiceKit/src/Messages/OWSBlockingManager.h b/SignalServiceKit/src/Messages/OWSBlockingManager.h index 03ac96c63..56757f75e 100644 --- a/SignalServiceKit/src/Messages/OWSBlockingManager.h +++ b/SignalServiceKit/src/Messages/OWSBlockingManager.h @@ -4,7 +4,6 @@ NS_ASSUME_NONNULL_BEGIN - extern NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange; // This class can be safely accessed and used from any thread. diff --git a/SignalShareExtension/ShareViewController.swift b/SignalShareExtension/ShareViewController.swift index 2a3db816f..1ada37614 100644 --- a/SignalShareExtension/ShareViewController.swift +++ b/SignalShareExtension/ShareViewController.swift @@ -20,6 +20,7 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE private var hasInitialRootViewController = false private var isReadyForAppExtensions = false + private var areVersionMigrationsComplete = false private var progressPoller: ProgressPoller? var loadViewController: SAELoadViewController? @@ -98,7 +99,11 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE // performUpdateCheck must be invoked after Environment has been initialized because // upgrade process may depend on Environment. - VersionMigrations.performUpdateCheck() + VersionMigrations.performUpdateCheck(completion: { + AssertIsOnMainThread() + + self.versionMigrationsDidComplete() + }) self.isNavigationBarHidden = true @@ -186,12 +191,44 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE } } + @objc + func versionMigrationsDidComplete() { + AssertIsOnMainThread() + + Logger.debug("\(self.logTag) \(#function)") + + areVersionMigrationsComplete = true + + checkIsAppReady() + } + @objc func storageIsReady() { AssertIsOnMainThread() Logger.debug("\(self.logTag) \(#function)") + checkIsAppReady() + } + + @objc + func checkIsAppReady() { + AssertIsOnMainThread() + + // App isn't ready until storage is ready AND all version migrations are complete. + guard areVersionMigrationsComplete else { + return + } + guard OWSStorage.isStorageReady() else { + return + } + guard AppReadiness.isAppReady() else { + // Only mark the app as ready once. + return + } + + Logger.debug("\(self.logTag) \(#function)") + // Note that this does much more than set a flag; // it will also run all deferred blocks. AppReadiness.setAppIsReady()