Some improvement to observing user changes.
This commit is contained in:
parent
ea5be8efcb
commit
fcaa5d1d8c
@ -35,6 +35,7 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
|||||||
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *mainContext))mocBlock;
|
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *mainContext))mocBlock;
|
||||||
+ (BOOL)managedObjectContextPerformBlock:(void ( ^ )(NSManagedObjectContext *context))mocBlock;
|
+ (BOOL)managedObjectContextPerformBlock:(void ( ^ )(NSManagedObjectContext *context))mocBlock;
|
||||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *context))mocBlock;
|
+ (BOOL)managedObjectContextPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *context))mocBlock;
|
||||||
|
+ (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock;
|
||||||
|
|
||||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
||||||
- (void)deleteAndResetStore;
|
- (void)deleteAndResetStore;
|
||||||
|
@ -131,6 +131,25 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock {
|
||||||
|
|
||||||
|
NSManagedObjectContext *privateManagedObjectContextIfReady = [[self get] privateManagedObjectContextIfReady];
|
||||||
|
if (!privateManagedObjectContextIfReady)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
return PearlAddNotificationObserver( NSManagedObjectContextObjectsDidChangeNotification, privateManagedObjectContextIfReady, nil,
|
||||||
|
^(id host, NSNotification *note) {
|
||||||
|
NSMutableDictionary *affectedObjects = [NSMutableDictionary new];
|
||||||
|
for (NSManagedObject *object in note.userInfo[NSInsertedObjectsKey])
|
||||||
|
affectedObjects[object.objectID] = NSInsertedObjectsKey;
|
||||||
|
for (NSManagedObject *object in note.userInfo[NSUpdatedObjectsKey])
|
||||||
|
affectedObjects[object.objectID] = NSUpdatedObjectsKey;
|
||||||
|
for (NSManagedObject *object in note.userInfo[NSDeletedObjectsKey])
|
||||||
|
affectedObjects[object.objectID] = NSDeletedObjectsKey;
|
||||||
|
changedBlock( affectedObjects );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
||||||
|
|
||||||
[self loadStore];
|
[self loadStore];
|
||||||
@ -196,13 +215,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|||||||
|
|
||||||
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
|
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
|
||||||
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
|
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
|
||||||
|
if ([self.mainManagedObjectContext respondsToSelector:@selector( automaticallyMergesChangesFromParent )]) // iOS 10+
|
||||||
|
self.mainManagedObjectContext.automaticallyMergesChangesFromParent = YES;
|
||||||
|
else
|
||||||
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
||||||
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
||||||
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainManagedObjectContext, NSNotification *note) {
|
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainContext, NSNotification *note) {
|
||||||
[mainManagedObjectContext performBlock:^{
|
[mainContext performBlock:^{
|
||||||
@try {
|
@try {
|
||||||
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
|
[mainContext mergeChangesFromContextDidSaveNotification:note];
|
||||||
}
|
}
|
||||||
@catch (NSException *exception) {
|
@catch (NSException *exception) {
|
||||||
err( @"While merging changes:\n%@", [exception fullDescription] );
|
err( @"While merging changes:\n%@", [exception fullDescription] );
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
- (BOOL)saveToStore {
|
- (BOOL)saveToStore {
|
||||||
|
|
||||||
__block BOOL success = YES;
|
__block BOOL success = YES;
|
||||||
if ([self hasChanges]) {
|
if ([self hasChanges])
|
||||||
[self performBlockAndWait:^{
|
[self performBlockAndWait:^{
|
||||||
@try {
|
@try {
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
@ -28,7 +28,6 @@
|
|||||||
err( @"While saving: %@", [exception fullDescription] );
|
err( @"While saving: %@", [exception fullDescription] );
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
|
||||||
|
|
||||||
return success && (!self.parentContext || [self.parentContext saveToStore]);
|
return success && (!self.parentContext || [self.parentContext saveToStore]);
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
@implementation MPUsersViewController {
|
@implementation MPUsersViewController {
|
||||||
NSString *_masterPasswordChoice;
|
NSString *_masterPasswordChoice;
|
||||||
NSOperationQueue *_afterUpdates;
|
NSOperationQueue *_afterUpdates;
|
||||||
|
__weak id _contextChangedObserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
@ -90,15 +91,6 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
self.userSelectionContainer.visible = NO;
|
self.userSelectionContainer.visible = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewWillDisappear:(BOOL)animated {
|
|
||||||
|
|
||||||
[super viewWillDisappear:animated];
|
|
||||||
|
|
||||||
PearlRemoveNotificationObservers();
|
|
||||||
|
|
||||||
[self.marqueeTipTimer invalidate];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidAppear:(BOOL)animated {
|
- (void)viewDidAppear:(BOOL)animated {
|
||||||
|
|
||||||
[super viewDidAppear:animated];
|
[super viewDidAppear:animated];
|
||||||
@ -130,6 +122,15 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
|||||||
((MPWebViewController *)segue.destinationViewController).initialURL = [NSURL URLWithString:@"http://thanks.lhunath.com"];
|
((MPWebViewController *)segue.destinationViewController).initialURL = [NSURL URLWithString:@"http://thanks.lhunath.com"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)viewWillDisappear:(BOOL)animated {
|
||||||
|
|
||||||
|
[super viewWillDisappear:animated];
|
||||||
|
|
||||||
|
[self removeObservers];
|
||||||
|
|
||||||
|
[self.marqueeTipTimer invalidate];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - UITextFieldDelegate
|
#pragma mark - UITextFieldDelegate
|
||||||
|
|
||||||
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
||||||
@ -465,25 +466,23 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
|||||||
- (void)deleteUser:(NSManagedObjectID *)userID {
|
- (void)deleteUser:(NSManagedObjectID *)userID {
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPUserEntity
|
MPUserEntity *user = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||||
*user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
if (!user)
|
||||||
if (!user_)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[context deleteObject:user_];
|
[context deleteObject:user];
|
||||||
[context saveToStore];
|
[context saveToStore];
|
||||||
[self reloadUsers]; // I do NOT understand why our ObjectsDidChangeNotification isn't firing on saveToStore.
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resetUser:(NSManagedObjectID *)userID avatar:(MPAvatarCell *)avatarCell {
|
- (void)resetUser:(NSManagedObjectID *)userID avatar:(MPAvatarCell *)avatarCell {
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
MPUserEntity *user = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||||
if (!user_)
|
if (!user)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
|
[[MPiOSAppDelegate get] changeMasterPasswordFor:user saveInContext:context didResetBlock:^{
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
|
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
|
||||||
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
|
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
|
||||||
@ -645,15 +644,21 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)registerObservers {
|
- (void)removeObservers {
|
||||||
|
|
||||||
[self removeKeyPathObservers];
|
[self removeKeyPathObservers];
|
||||||
|
PearlRemoveNotificationObservers();
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:_contextChangedObserver];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)registerObservers {
|
||||||
|
|
||||||
|
[self removeObservers];
|
||||||
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
|
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
|
||||||
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
|
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
|
||||||
[_self updateAvatarVisibility];
|
[_self updateAvatarVisibility];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
PearlRemoveNotificationObservers();
|
|
||||||
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
|
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
|
||||||
^(MPUsersViewController *self, NSNotification *note) {
|
^(MPUsersViewController *self, NSNotification *note) {
|
||||||
self.userSelectionContainer.visible = NO;
|
self.userSelectionContainer.visible = NO;
|
||||||
@ -675,37 +680,32 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
|||||||
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
|
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
|
||||||
} );
|
} );
|
||||||
|
|
||||||
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
if ((_contextChangedObserver = [MPiOSAppDelegate managedObjectContextChanged:^(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects) {
|
||||||
[UIView animateWithDuration:0.3f animations:^{
|
if ([[[affectedObjects allKeys] filteredArrayUsingPredicate:
|
||||||
self.avatarCollectionView.visible = mainContext != nil;
|
[NSPredicate predicateWithBlock:^BOOL(NSManagedObjectID *objectID, NSDictionary *bindings) {
|
||||||
}];
|
return [objectID.entity.name isEqualToString:NSStringFromClass( [MPUserEntity class] )];
|
||||||
if (mainContext && self.storeLoadingActivity.isAnimating)
|
|
||||||
[self.storeLoadingActivity stopAnimating];
|
|
||||||
if (!mainContext && !self.storeLoadingActivity.isAnimating)
|
|
||||||
[self.storeLoadingActivity startAnimating];
|
|
||||||
|
|
||||||
if (mainContext)
|
|
||||||
PearlAddNotificationObserver( NSManagedObjectContextObjectsDidChangeNotification, mainContext, nil,
|
|
||||||
^(MPUsersViewController *self, NSNotification *note) {
|
|
||||||
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
|
|
||||||
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
|
|
||||||
if ([[NSSetUnion( insertedObjects, deletedObjects )
|
|
||||||
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
|
|
||||||
return [evaluatedObject isKindOfClass:[MPUserEntity class]];
|
|
||||||
}]] count])
|
}]] count])
|
||||||
[self reloadUsers];
|
[self reloadUsers];
|
||||||
} );
|
}]))
|
||||||
|
[UIView animateWithDuration:0.3f animations:^{
|
||||||
|
self.avatarCollectionView.visible = YES;
|
||||||
|
[self.storeLoadingActivity stopAnimating];
|
||||||
|
}];
|
||||||
|
else
|
||||||
|
[UIView animateWithDuration:0.3f animations:^{
|
||||||
|
self.avatarCollectionView.visible = NO;
|
||||||
|
[self.storeLoadingActivity startAnimating];
|
||||||
|
}];
|
||||||
|
|
||||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
|
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
|
||||||
^(MPUsersViewController *self, NSNotification *note) {
|
^(MPUsersViewController *self, NSNotification *note) {
|
||||||
self.userIDs = nil;
|
self.userIDs = nil;
|
||||||
} );
|
} );
|
||||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
|
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
|
||||||
^(MPUsersViewController *self, NSNotification *note) {
|
^(MPUsersViewController *self, NSNotification *note) {
|
||||||
PearlMainQueue( ^{
|
|
||||||
[self registerObservers];
|
[self registerObservers];
|
||||||
[self reloadUsers];
|
[self reloadUsers];
|
||||||
} );
|
} );
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reloadUsers {
|
- (void)reloadUsers {
|
||||||
|
Loading…
Reference in New Issue
Block a user