Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registering to receive notifications of remote CloudKit changes not working

I have just finished setting up CoreData+CloudKit using the new iOS 13 NSPersistentCloudKitContainer. It works really nicely in that I can use auto-generated CoreData classes for property access and local storage, and the NSPersistentCloudKitContainer automatically synchronises changes between devices. The problem I am having is with getting notified of remote changes. I have checked the Apple documentation and this states that you tell the NSPersistentCloudKitContainer's NSPersistentStoreDescription that you want it to send the notification, and then register other objects as observers of this notification. I have done this and added a test method to show when remote changes were detected. The alert generated by the test method is never generated, but if I kill the app and re-open it, the changes are there immediately. So I believe the remote changes are being synchronised and integrated into the local CoreData storage, but the notification is not working. I have added the Background Modes entitlement to my target and selected the Remote notification mode. Code is below. Any help would be gratefully received!

Setting the option to send the notification:

- (NSPersistentCloudKitContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentCloudKitContainer alloc] initWithName:@"<redacted>"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    // ...
                }
                else {
                    // ...

                    [storeDescription setOption:@(YES) forKey:NSPersistentStoreRemoteChangeNotificationPostOptionKey];

                    // ...
                }
            }];
        }
    }

    return _persistentContainer;
}

Registering to receive the notification:

- (void)viewDidLoad {
    [super viewDidLoad];

    // ...

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changes) name:NSPersistentStoreRemoteChangeNotification object:[CoreDataFunctions persistentContainer]];
}

Test method to respond to changes:

- (void)changes {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Changes received" message:nil preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
    [alert addAction:ok];
    [self presentViewController:alert animated:YES completion:nil];
}
like image 956
mashers Avatar asked Oct 31 '19 10:10

mashers


2 Answers

You observed the persistent container instead of the store coordinator, it should be this:

[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(persistentStoreRemoteChangeNotification:) name:NSPersistentStoreRemoteChangeNotification object:_persistentContainer.persistentStoreCoordinator];
like image 173
malhal Avatar answered Nov 17 '22 20:11

malhal


Wherever you access your app’s persistent CloudKit container to grab the viewContext, you need to set the automaticallyMergesChangesFromParent property to true.

lazy var managedContext: NSManagedObjectContext = {
    self.storeContainer.viewContext.automaticallyMergesChangesFromParent = true
    return self.storeContainer.viewContext
}()

Making this one-line change will enable the app (which is supported by NSFetchedResultsController) to update the UI in response to remote data changes…

like image 2
Jiafu Zhang Avatar answered Nov 17 '22 19:11

Jiafu Zhang



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!