Monitoring replication iOS

I’m trying to get notified when my continuous replication changes state. I’ve tried the code below, but I only ever get 1 notification (when it starts). What am I doing wrong please?

[[NSNotificationCenter defaultCenter] addObserverForName:kCBLReplicationChangeNotification object:repl queue:nil usingBlock:^(NSNotification *notification) {

    NSString *status;
    if (repl.status == kCBLReplicationActive) {
        NSLog(@"Repication in progress");
        status = @"in-progrss";
    } else if (repl.status == kCBLReplicationOffline) {
        NSLog(@"Sync in offline");
        status = @"offline";
    } else if (repl.status == kCBLReplicationStopped) {
        NSLog(@"Sync in stopped");
        status = @"in-stopped";
    } else if (repl.status == kCBLReplicationIdle) {
        NSLog(@"Sync in idle");
        status = @"in-idle";
    }

    NSError *error = repl.lastError;
    if(error) {
        status = @"error";
        NSLog(@"replication error %ld", (long)error.code);
    }

    NSDictionary *dictionary = @{
                                 @"type": type,
                                 @"changesCount": @(repl.changesCount),
                                 @"completedChangesCount": @(repl.completedChangesCount),
                                 @"running": @(repl.running),
                                 @"status": status,
                                 @"suspended": @(repl.suspended),
                                 };

    [self sendEventWithName:@"replicationChanged" body:dictionary];

}];

Usually when this problem occurs it’s because the object registered as the observer got dealloced. However, you’re using the new-ish API that registers a block as the observer; I’m not entirely sure how ref-counting works with that.

Maybe try changing this to the traditional object/selector style, then make sure the object has strong references to it so it doesn’t get dealloced.

Thanks for the reply Jens.

I have tried doing it the other way, but get the same result. Just a single callback per continuous replication, with status kCBLReplicationOffline, and never anything else.

[[NSNotificationCenter defaultCenter] addObserver: self
                                         selector: @selector(replicationChanged:)
                                             name: kCBLReplicationChangeNotification
                                           object: repl];

...

- (void) replicationChanged: (NSNotification*)notification {
CBLReplication *repl = notification.object;

NSString *status;
if (repl.status == kCBLReplicationActive) {
    NSLog(@"Repication in progress");
    status = @"in-progrss";
} else if (repl.status == kCBLReplicationOffline) {
    NSLog(@"Sync in offline");
    status = @"offline";
} else if (repl.status == kCBLReplicationStopped) {
    NSLog(@"Sync in stopped");
    status = @"in-stopped";
} else if (repl.status == kCBLReplicationIdle) {
    NSLog(@"Sync in idle");
    status = @"in-idle";
}

NSError *error = repl.lastError;
if(error) {
    status = @"error";
    NSLog(@"replication error %ld", (long)error.code);
}

NSDictionary *dictionary = @{
                             @"changesCount": @(repl.changesCount),
                             @"completedChangesCount": @(repl.completedChangesCount),
                             @"running": @(repl.running),
                             @"status": status,
                             @"suspended": @(repl.suspended),
                             };

[self sendEventWithName:@"replicationChanged" body:dictionary];
}

Replication is working fine though.

Any more ideas? Is there a working example out there somewhere? I simply can’t get it to work with CBL 1.4 - there’s only ever one event.

Here’s my code.

Oh, you didn’t say that this is in React Native, not in a normal app!

What thread does this object get called on? Is the thread running a runloop? Couchbase Lite notifications are scheduled via the runloop, so without that you won’t get them.

Alternatively you can set the Manager’s dispatchQueue property to a serial queue, and notifications will be delivered there. But then you need to always call Couchbase Lite methods from that queue, to avoid threading problems.

Ha, that’s a good question, thanks for the reply.

Why method do you mean? startContinuousReplication or replicationChanged?

startContinuousReplication gets called by RN, i don’t know which thread, I don’t even know how to find out at the moment. And replicationChanged, well, doesn’t get called at all al the moment which is the problem i’m trying to sort out.

Well, that’s your problem then. React’s thread probably doesn’t run a runloop.

In your case I would use a dispatch queue. Create your own, and then wrap dispatch_sync around the calls to Couchbase Lite.

Then you’ll have to be careful calling in the other direction, since callbacks from Couchbase Lite will occur on that queue (I.e. On a random background thread) and it might not be safe to call into React from there.

I had a look around the react-native docs and found this. Specifically it says

The - (dispatch_queue_t)methodQueue method allows the native module to specify which queue its methods should be run on

So I added one to my RN module here, and I can see at least form the logging that it looks like all method are now invoked on my new thread (called RNCBL.Queue), for example:

[info][tid:RNCBL.Queue][ReactCBLite.m:38] Launching Couchbase Lite...

It works if I specify the main thread. Thanks for your help.