diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index c1e879fea..191dfd43e 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -295,7 +295,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { name:YapDatabaseModifiedNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(yapDatabaseModified:) + selector:@selector(yapDatabaseModifiedExternally:) name:YapDatabaseModifiedExternallyNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self @@ -2782,6 +2782,15 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { return _editingDatabaseConnection; } +- (void)yapDatabaseModifiedExternally:(NSNotification *)notification +{ + OWSAssert([NSThread isMainThread]); + + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + + [self resetMappings]; +} + - (void)yapDatabaseModified:(NSNotification *)notification { OWSAssert([NSThread isMainThread]); @@ -2794,6 +2803,8 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { return; } + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + // HACK to work around radar #28167779 // "UICollectionView performBatchUpdates can trigger a crash if the collection view is flagged for layout" // more: https://github.com/PSPDFKit-labs/radar.apple.com/tree/master/28167779%20-%20CollectionViewBatchingIssue diff --git a/Signal/src/ViewControllers/HomeViewController.m b/Signal/src/ViewControllers/HomeViewController.m index 52b375728..6d8701dd8 100644 --- a/Signal/src/ViewControllers/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeViewController.m @@ -111,7 +111,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; _blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]]; // Ensure ExperienceUpgradeFinder has been initialized. - ExperienceUpgradeFinder.sharedManager; + [ExperienceUpgradeFinder sharedManager]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(blockedPhoneNumbersDidChange:) @@ -138,7 +138,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; name:YapDatabaseModifiedNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(yapDatabaseModified:) + selector:@selector(yapDatabaseModifiedExternally:) name:YapDatabaseModifiedExternallyNotification object:nil]; } @@ -923,12 +923,25 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; return _uiDatabaseConnection; } +- (void)yapDatabaseModifiedExternally:(NSNotification *)notification +{ + OWSAssert([NSThread isMainThread]); + + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + + [self resetMappings]; +} + - (void)yapDatabaseModified:(NSNotification *)notification { + OWSAssert([NSThread isMainThread]); + if (!self.shouldObserveDBModifications) { return; } + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + NSArray *notifications = [self.uiDatabaseConnection beginLongLivedReadTransaction]; if (![[self.uiDatabaseConnection ext:TSThreadDatabaseViewExtensionName] hasChangesForGroup:self.currentGrouping diff --git a/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m b/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m index 1c3d41dcd..0d40ad9c9 100644 --- a/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m +++ b/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m @@ -65,11 +65,11 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yapDatabaseModified:) name:YapDatabaseModifiedNotification - object:self.dbConnection.database]; + object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(yapDatabaseModified:) + selector:@selector(yapDatabaseModifiedExternally:) name:YapDatabaseModifiedExternallyNotification - object:self.dbConnection.database]; + object:nil]; self.refreshControl = [UIRefreshControl new]; [self.refreshControl addTarget:self action:@selector(refreshDevices) forControlEvents:UIControlEventValueChanged]; @@ -200,8 +200,29 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1; #pragma mark - Table view data source +- (void)yapDatabaseModifiedExternally:(NSNotification *)notification +{ + OWSAssert([NSThread isMainThread]); + + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + + // External database modifications can't be converted into incremental updates, + // so rebuild everything. This is expensive and usually isn't necessary, but + // there's no alternative. + [self.dbConnection beginLongLivedReadTransaction]; + [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + [self.deviceMappings updateWithTransaction:transaction]; + }]; + + [self.tableView reloadData]; +} + - (void)yapDatabaseModified:(NSNotification *)notification { + OWSAssert([NSThread isMainThread]); + + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + NSArray *notifications = [self.dbConnection beginLongLivedReadTransaction]; [self setupEditButton]; diff --git a/Signal/src/util/MainAppContext.m b/Signal/src/util/MainAppContext.m index 15c1273b2..d4e825ecb 100644 --- a/Signal/src/util/MainAppContext.m +++ b/Signal/src/util/MainAppContext.m @@ -12,6 +12,75 @@ NS_ASSUME_NONNULL_BEGIN @implementation MainAppContext +- (instancetype)init +{ + self = [super init]; + + if (!self) { + return self; + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationDidEnterBackground:) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillResignActive:) + name:UIApplicationWillResignActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillTerminate:) + name:UIApplicationWillTerminateNotification + object:nil]; + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - Notifications + +- (void)applicationWillEnterForeground:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); +} + +- (void)applicationDidEnterBackground:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + [DDLog flushLog]; +} + +- (void)applicationWillResignActive:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + [DDLog flushLog]; +} + +- (void)applicationDidBecomeActive:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); +} + +- (void)applicationWillTerminate:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + [DDLog flushLog]; +} + +#pragma mark - + - (BOOL)isMainApp { return YES; diff --git a/SignalMessaging/Views/ThreadViewHelper.m b/SignalMessaging/Views/ThreadViewHelper.m index 5af480c06..ab8748b15 100644 --- a/SignalMessaging/Views/ThreadViewHelper.m +++ b/SignalMessaging/Views/ThreadViewHelper.m @@ -3,6 +3,7 @@ // #import "ThreadViewHelper.h" +#import #import #import #import @@ -16,9 +17,12 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) YapDatabaseConnection *uiDatabaseConnection; @property (nonatomic) YapDatabaseViewMappings *threadMappings; +@property (nonatomic) BOOL shouldObserveDBModifications; @end +#pragma mark - + @implementation ThreadViewHelper - (instancetype)init @@ -48,49 +52,117 @@ NS_ASSUME_NONNULL_BEGIN [[YapDatabaseViewMappings alloc] initWithGroups:@[ grouping ] view:TSThreadDatabaseViewExtensionName]; [self.threadMappings setIsReversed:YES forGroup:grouping]; - __weak ThreadViewHelper *weakSelf = self; - [self.uiDatabaseConnection asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction) { - [self.threadMappings updateWithTransaction:transaction]; + self.uiDatabaseConnection = [TSStorageManager.sharedManager newDatabaseConnection]; + [self.uiDatabaseConnection beginLongLivedReadTransaction]; - dispatch_async(dispatch_get_main_queue(), ^{ - [weakSelf updateThreads]; - [weakSelf.delegate threadListDidChange]; - }); - }]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationDidEnterBackground:) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + + self.shouldObserveDBModifications + = !(CurrentAppContext().isMainApp && CurrentAppContext().mainApplicationState == UIApplicationStateBackground); } -#pragma mark - Database +- (void)applicationWillEnterForeground:(NSNotification *)notification +{ + self.shouldObserveDBModifications = YES; +} -- (YapDatabaseConnection *)uiDatabaseConnection +- (void)applicationDidEnterBackground:(NSNotification *)notification +{ + self.shouldObserveDBModifications = NO; +} + +// Don't observe database change notifications when the app is in the background. +// +// Instead, rebuild model state when app enters foreground. +- (void)setShouldObserveDBModifications:(BOOL)shouldObserveDBModifications { - NSAssert([NSThread isMainThread], @"Must access uiDatabaseConnection on main thread!"); - if (!_uiDatabaseConnection) { - YapDatabase *database = TSStorageManager.sharedManager.database; - _uiDatabaseConnection = [database newConnection]; - [_uiDatabaseConnection beginLongLivedReadTransaction]; + if (_shouldObserveDBModifications == shouldObserveDBModifications) { + return; + } + + _shouldObserveDBModifications = shouldObserveDBModifications; + + if (shouldObserveDBModifications) { + [self.uiDatabaseConnection beginLongLivedReadTransaction]; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + [self.threadMappings updateWithTransaction:transaction]; + }]; + [self updateThreads]; + [self.delegate threadListDidChange]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yapDatabaseModified:) name:YapDatabaseModifiedNotification - object:database]; + object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(yapDatabaseModified:) + selector:@selector(yapDatabaseModifiedExternally:) name:YapDatabaseModifiedExternallyNotification - object:database]; + object:nil]; + } else { + [[NSNotificationCenter defaultCenter] removeObserver:self name:YapDatabaseModifiedNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:YapDatabaseModifiedExternallyNotification + object:nil]; } +} + +#pragma mark - Database + +- (YapDatabaseConnection *)uiDatabaseConnection +{ + OWSAssert([NSThread isMainThread]); + return _uiDatabaseConnection; } +- (void)yapDatabaseModifiedExternally:(NSNotification *)notification +{ + OWSAssert([NSThread isMainThread]); + + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + + // External database modifications can't be converted into incremental updates, + // so rebuild everything. This is expensive and usually isn't necessary, but + // there's no alternative. + [self.uiDatabaseConnection beginLongLivedReadTransaction]; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + [self.threadMappings updateWithTransaction:transaction]; + }]; + + [self updateThreads]; + [self.delegate threadListDidChange]; +} + - (void)yapDatabaseModified:(NSNotification *)notification { OWSAssert([NSThread isMainThread]); + DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + NSArray *notifications = [self.uiDatabaseConnection beginLongLivedReadTransaction]; + + if (! + [[self.uiDatabaseConnection ext:TSMessageDatabaseViewExtensionName] hasChangesForNotifications:notifications]) { + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + [self.threadMappings updateWithTransaction:transaction]; + }]; + return; + } + NSArray *sectionChanges = nil; NSArray *rowChanges = nil; [[self.uiDatabaseConnection ext:TSThreadDatabaseViewExtensionName] getSectionChanges:§ionChanges rowChanges:&rowChanges forNotifications:notifications withMappings:self.threadMappings]; + if (sectionChanges.count == 0 && rowChanges.count == 0) { // Ignore irrelevant modifications. return; diff --git a/SignalShareExtension/ShareViewController.swift b/SignalShareExtension/ShareViewController.swift index 650b04290..58a69e88b 100644 --- a/SignalShareExtension/ShareViewController.swift +++ b/SignalShareExtension/ShareViewController.swift @@ -345,18 +345,24 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE // MARK: ShareViewDelegate, SAEFailedViewDelegate public func shareViewWasCompleted() { + Logger.info("\(self.logTag) \(#function)") + self.dismiss(animated: true) { self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) } } public func shareViewWasCancelled() { + Logger.info("\(self.logTag) \(#function)") + self.dismiss(animated: true) { self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) } } public func shareViewFailed(error: Error) { + Logger.info("\(self.logTag) \(#function)") + self.dismiss(animated: true) { self.extensionContext!.cancelRequest(withError: error) } diff --git a/SignalShareExtension/utils/ShareAppExtensionContext.m b/SignalShareExtension/utils/ShareAppExtensionContext.m index 6795771b4..8a383ff68 100644 --- a/SignalShareExtension/utils/ShareAppExtensionContext.m +++ b/SignalShareExtension/utils/ShareAppExtensionContext.m @@ -29,9 +29,57 @@ NS_ASSUME_NONNULL_BEGIN _rootViewController = rootViewController; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(extensionHostDidBecomeActive:) + name:NSExtensionHostDidBecomeActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(extensionHostWillResignActive:) + name:NSExtensionHostWillResignActiveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(extensionHostDidEnterBackground:) + name:NSExtensionHostDidEnterBackgroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(extensionHostWillEnterForeground:) + name:NSExtensionHostWillEnterForegroundNotification + object:nil]; + return self; } +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - Notifications + +- (void)extensionHostDidBecomeActive:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); +} + +- (void)extensionHostWillResignActive:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + [DDLog flushLog]; +} + +- (void)extensionHostDidEnterBackground:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + [DDLog flushLog]; +} + +- (void)extensionHostWillEnterForeground:(NSNotification *)notification +{ + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); +} + +#pragma mark - + - (BOOL)isMainApp { return NO;