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

pull/1/head
Matthew Chen 8 years ago
parent 3e09143a3e
commit be1fde905c

@ -60,6 +60,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
@property (nonatomic) UIWindow *screenProtectionWindow; @property (nonatomic) UIWindow *screenProtectionWindow;
@property (nonatomic) BOOL hasInitialRootViewController; @property (nonatomic) BOOL hasInitialRootViewController;
@property (nonatomic) BOOL areVersionMigrationsComplete;
@end @end
@ -167,7 +168,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
// performUpdateCheck must be invoked after Environment has been initialized because // performUpdateCheck must be invoked after Environment has been initialized because
// upgrade process may depend on Environment. // upgrade process may depend on Environment.
[VersionMigrations performUpdateCheck]; [VersionMigrations performUpdateCheckWithCompletion:^{
OWSAssertIsOnMainThread();
[self versionMigrationsDidComplete];
}];
// Accept push notification when app is not open // Accept push notification when app is not open
NSDictionary *remoteNotif = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; NSDictionary *remoteNotif = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
@ -819,11 +824,43 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
completionHandler:completionHandler]; completionHandler:completionHandler];
} }
- (void)versionMigrationsDidComplete
{
OWSAssertIsOnMainThread();
DDLogInfo(@"%@ versionMigrationsDidComplete", self.logTag);
self.areVersionMigrationsComplete = YES;
[self checkIfAppIsReady];
}
- (void)storageIsReady - (void)storageIsReady
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
DDLogInfo(@"%@ storageIsReady", self.logTag); 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]]; [OWSPreferences setIsRegistered:[TSAccountManager isRegistered]];
// Note that this does much more than set a flag; // Note that this does much more than set a flag;

@ -2,11 +2,15 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
NS_ASSUME_NONNULL_BEGIN
#define RECENT_CALLS_DEFAULT_KEY @"RPRecentCallsDefaultKey" #define RECENT_CALLS_DEFAULT_KEY @"RPRecentCallsDefaultKey"
typedef void (^VersionMigrationCompletion)(void);
@interface VersionMigrations : NSObject @interface VersionMigrations : NSObject
+ (void)performUpdateCheck; + (void)performUpdateCheckWithCompletion:(VersionMigrationCompletion)completion;
+ (BOOL)isVersion:(NSString *)thisVersionString + (BOOL)isVersion:(NSString *)thisVersionString
atLeast:(NSString *)openLowerBoundVersionString atLeast:(NSString *)openLowerBoundVersionString
@ -17,3 +21,5 @@
+ (BOOL)isVersion:(NSString *)thisVersionString lessThan:(NSString *)thatVersionString; + (BOOL)isVersion:(NSString *)thisVersionString lessThan:(NSString *)thatVersionString;
@end @end
NS_ASSUME_NONNULL_END

@ -14,6 +14,8 @@
#import <SignalServiceKit/TSNetworkManager.h> #import <SignalServiceKit/TSNetworkManager.h>
#import <YapDatabase/YapDatabase.h> #import <YapDatabase/YapDatabase.h>
NS_ASSUME_NONNULL_BEGIN
#define NEEDS_TO_REGISTER_PUSH_KEY @"Register For Push" #define NEEDS_TO_REGISTER_PUSH_KEY @"Register For Push"
#define NEEDS_TO_REGISTER_ATTRIBUTES @"Register Attributes" #define NEEDS_TO_REGISTER_ATTRIBUTES @"Register Attributes"
@ -28,11 +30,12 @@
#pragma mark Utility methods #pragma mark Utility methods
+ (void)performUpdateCheck + (void)performUpdateCheckWithCompletion:(VersionMigrationCompletion)completion
{ {
// performUpdateCheck must be invoked after Environment has been initialized because // performUpdateCheck must be invoked after Environment has been initialized because
// upgrade process may depend on Environment. // upgrade process may depend on Environment.
OWSAssert([Environment current]); OWSAssert([Environment current]);
OWSAssert(completion);
NSString *previousVersion = AppVersion.instance.lastAppVersion; NSString *previousVersion = AppVersion.instance.lastAppVersion;
NSString *currentVersion = AppVersion.instance.currentAppVersion; NSString *currentVersion = AppVersion.instance.currentAppVersion;
@ -47,6 +50,9 @@
OWSDatabaseMigrationRunner *runner = OWSDatabaseMigrationRunner *runner =
[[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]]; [[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]];
[runner assumeAllExistingMigrationsRun]; [runner assumeAllExistingMigrationsRun];
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
return; return;
} }
@ -79,7 +85,8 @@
[self clearBloomFilterCache]; [self clearBloomFilterCache];
} }
[[[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]] runAllOutstanding]; [[[OWSDatabaseMigrationRunner alloc] initWithStorageManager:[TSStorageManager sharedManager]]
runAllOutstandingWithCompletion:completion];
} }
+ (BOOL)isVersion:(NSString *)thisVersionString + (BOOL)isVersion:(NSString *)thisVersionString
@ -189,3 +196,5 @@
} }
@end @end
NS_ASSUME_NONNULL_END

@ -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" #import "OWS100RemoveTSRecipientsMigration.h"
@ -19,6 +19,8 @@ static NSString *const OWS100RemoveTSRecipientsMigrationId = @"100";
- (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction - (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
OWSAssert(transaction);
NSUInteger legacyRecipientCount = [transaction numberOfKeysInCollection:@"TSRecipient"]; NSUInteger legacyRecipientCount = [transaction numberOfKeysInCollection:@"TSRecipient"];
DDLogWarn(@"Removing %lu objects from TSRecipient collection", (unsigned long)legacyRecipientCount); DDLogWarn(@"Removing %lu objects from TSRecipient collection", (unsigned long)legacyRecipientCount);
[transaction removeAllObjectsInCollection:@"TSRecipient"]; [transaction removeAllObjectsInCollection:@"TSRecipient"];

@ -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" #import "OWS101ExistingUsersBlockOnIdentityChange.h"
@ -24,6 +24,8 @@ static NSString *const OWS101ExistingUsersBlockOnIdentityChangeMigrationId = @"1
- (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction - (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
OWSAssert(transaction);
OWSFail(@"[OWS101ExistingUsersBlockOnIdentityChange] has been obviated."); OWSFail(@"[OWS101ExistingUsersBlockOnIdentityChange] has been obviated.");
} }

@ -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" #import "OWS102MoveLoggingPreferenceToUserDefaults.h"
@ -20,6 +20,8 @@ static NSString *const OWS102MoveLoggingPreferenceToUserDefaultsMigrationId = @"
- (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction - (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
OWSAssert(transaction);
DDLogWarn(@"[OWS102MoveLoggingPreferenceToUserDefaultsMigrationId] copying existing logging preference to " DDLogWarn(@"[OWS102MoveLoggingPreferenceToUserDefaultsMigrationId] copying existing logging preference to "
@"NSUserDefaults"); @"NSUserDefaults");

@ -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" #import "OWS103EnableVideoCalling.h"
@ -18,8 +18,10 @@ static NSString *const OWS103EnableVideoCallingMigrationId = @"103";
} }
// Override parent migration // Override parent migration
- (void)runUp - (void)runUpWithCompletion:(OWSDatabaseMigrationCompletion)completion
{ {
OWSAssert(completion);
DDLogWarn(@"%@ running migration...", self.logTag); DDLogWarn(@"%@ running migration...", self.logTag);
if ([TSAccountManager isRegistered]) { if ([TSAccountManager isRegistered]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 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) { success:^(NSURLSessionDataTask *task, id responseObject) {
DDLogInfo(@"%@ successfully ran", self.logTag); DDLogInfo(@"%@ successfully ran", self.logTag);
[self save]; [self save];
completion();
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
if (!IsNSErrorNetworkFailure(error)) { if (!IsNSErrorNetworkFailure(error)) {
OWSProdError([OWSAnalyticsEvents errorEnableVideoCallingRequestFailed]); OWSProdError([OWSAnalyticsEvents errorEnableVideoCallingRequestFailed]);
} }
DDLogError(@"%@ failed with error: %@", self.logTag, error); DDLogError(@"%@ failed with error: %@", self.logTag, error);
completion();
}]; }];
}); });
} else { } else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
DDLogInfo(@"%@ skipping; not registered", self.logTag); DDLogInfo(@"%@ skipping; not registered", self.logTag);
[self save]; [self save];
completion();
}); });
} }
} }

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

@ -1,5 +1,5 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
import Foundation import Foundation
@ -20,7 +20,7 @@ public class OWS106EnsureProfileComplete: OWSDatabaseMigration {
// Overriding runUp since we have some specific completion criteria which // Overriding runUp since we have some specific completion criteria which
// is more likely to fail since it involves network requests. // 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 { guard type(of: self).sharedCompleteRegistrationFixerJob == nil else {
owsFail("\(self.TAG) should only be called once.") owsFail("\(self.TAG) should only be called once.")
return return
@ -29,6 +29,8 @@ public class OWS106EnsureProfileComplete: OWSDatabaseMigration {
let job = CompleteRegistrationFixerJob(completionHandler: { let job = CompleteRegistrationFixerJob(completionHandler: {
Logger.info("\(self.TAG) Completed. Saving.") Logger.info("\(self.TAG) Completed. Saving.")
self.save() self.save()
completion()
}) })
type(of: self).sharedCompleteRegistrationFixerJob = job type(of: self).sharedCompleteRegistrationFixerJob = job

@ -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> #import <SignalServiceKit/TSYapDatabaseObject.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
typedef void (^OWSDatabaseMigrationCompletion)(void);
@class TSStorageManager; @class TSStorageManager;
@interface OWSDatabaseMigration : TSYapDatabaseObject @interface OWSDatabaseMigration : TSYapDatabaseObject
@ -18,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
// Blocking migrations running too long will crash the app, effectively bricking install // Blocking migrations running too long will crash the app, effectively bricking install
// because the user will never get past it. // because the user will never get past it.
// If you must write a launch-blocking migration, override runUp. // If you must write a launch-blocking migration, override runUp.
- (void)runUp; - (void)runUpWithCompletion:(OWSDatabaseMigrationCompletion)completion;
@end @end

@ -46,8 +46,10 @@ NS_ASSUME_NONNULL_BEGIN
OWSRaiseException(NSInternalInconsistencyException, @"Must override %@ in subclass", NSStringFromSelector(_cmd)); OWSRaiseException(NSInternalInconsistencyException, @"Must override %@ in subclass", NSStringFromSelector(_cmd));
} }
- (void)runUp - (void)runUpWithCompletion:(OWSDatabaseMigrationCompletion)completion
{ {
OWSAssert(completion);
[self.storageManager.newDatabaseConnection [self.storageManager.newDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self runUpWithTransaction:transaction]; [self runUpWithTransaction:transaction];
@ -55,6 +57,8 @@ NS_ASSUME_NONNULL_BEGIN
completionBlock:^{ completionBlock:^{
DDLogInfo(@"Completed migration %@", self.uniqueId); DDLogInfo(@"Completed migration %@", self.uniqueId);
[self save]; [self save];
completion();
}]; }];
} }

@ -4,6 +4,8 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
typedef void (^OWSDatabaseMigrationCompletion)(void);
@class TSStorageManager; @class TSStorageManager;
@interface OWSDatabaseMigrationRunner : NSObject @interface OWSDatabaseMigrationRunner : NSObject
@ -15,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* Run any outstanding version migrations. * Run any outstanding version migrations.
*/ */
- (void)runAllOutstanding; - (void)runAllOutstandingWithCompletion:(OWSDatabaseMigrationCompletion)completion;
/** /**
* On new installations, no need to migrate anything. * On new installations, no need to migrate anything.

@ -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 - (void)runMigrations:(NSArray<OWSDatabaseMigration *> *)migrations
completion:(OWSDatabaseMigrationCompletion)completion
{ {
OWSAssert(migrations); OWSAssert(migrations);
OWSAssert(completion);
NSMutableArray<OWSDatabaseMigration *> *migrationsToRun = [NSMutableArray new];
for (OWSDatabaseMigration *migration in migrations) { for (OWSDatabaseMigration *migration in migrations) {
if ([OWSDatabaseMigration fetchObjectWithUniqueID:migration.uniqueId]) { if ([OWSDatabaseMigration fetchObjectWithUniqueID:migration.uniqueId]) {
DDLogDebug(@"%@ Skipping previously run migration: %@", self.logTag, migration); DDLogDebug(@"%@ Skipping previously run migration: %@", self.logTag, migration);
} else { } else {
DDLogWarn(@"%@ Running migration: %@", self.logTag, migration); 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];
} }
} }
} }

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

@ -20,6 +20,7 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE
private var hasInitialRootViewController = false private var hasInitialRootViewController = false
private var isReadyForAppExtensions = false private var isReadyForAppExtensions = false
private var areVersionMigrationsComplete = false
private var progressPoller: ProgressPoller? private var progressPoller: ProgressPoller?
var loadViewController: SAELoadViewController? var loadViewController: SAELoadViewController?
@ -98,7 +99,11 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE
// performUpdateCheck must be invoked after Environment has been initialized because // performUpdateCheck must be invoked after Environment has been initialized because
// upgrade process may depend on Environment. // upgrade process may depend on Environment.
VersionMigrations.performUpdateCheck() VersionMigrations.performUpdateCheck(completion: {
AssertIsOnMainThread()
self.versionMigrationsDidComplete()
})
self.isNavigationBarHidden = true 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 @objc
func storageIsReady() { func storageIsReady() {
AssertIsOnMainThread() AssertIsOnMainThread()
Logger.debug("\(self.logTag) \(#function)") 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; // Note that this does much more than set a flag;
// it will also run all deferred blocks. // it will also run all deferred blocks.
AppReadiness.setAppIsReady() AppReadiness.setAppIsReady()

Loading…
Cancel
Save