Don't mark app as ready until all version migrations are done.

This commit is contained in:
Matthew Chen 2018-01-30 11:27:44 -05:00
parent 3e09143a3e
commit be1fde905c
15 changed files with 167 additions and 20 deletions

View file

@ -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;

View file

@ -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

View file

@ -14,6 +14,8 @@
#import <SignalServiceKit/TSNetworkManager.h>
#import <YapDatabase/YapDatabase.h>
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

View file

@ -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"];

View file

@ -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.");
}

View file

@ -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");

View file

@ -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();
});
}
}

View file

@ -26,6 +26,8 @@ static NSString *const OWS104CreateRecipientIdentitiesMigrationId = @"104";
- (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
NSMutableDictionary<NSString *, NSData *> *identityKeys = [NSMutableDictionary new];
[transaction

View file

@ -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

View file

@ -1,11 +1,13 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <SignalServiceKit/TSYapDatabaseObject.h>
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

View file

@ -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();
}];
}

View file

@ -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.

View file

@ -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<OWSDatabaseMigration *> *)migrations
completion:(OWSDatabaseMigrationCompletion)completion
{
OWSAssert(migrations);
OWSAssert(completion);
NSMutableArray<OWSDatabaseMigration *> *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];
}
}
}

View file

@ -4,7 +4,6 @@
NS_ASSUME_NONNULL_BEGIN
extern NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange;
// This class can be safely accessed and used from any thread.

View file

@ -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()