CBLListener does not implement 'start' method in iOS...cannot start CB Lite REST server

#1

Hello!

I am trying to follow along with the steps for creating a Peer-to-Peer replication, but the start method of CBLListener is not implemented in the CouchbaseLiteListener framework. Here is the Couchbase Wiki I am following - https://github.com/couchbase/couchbase-lite-ios/wiki/Guide:-REST

The two header files in the framework are CBLListener.h and CouchbaseLiteListener.h. Here is my implementation:

CBLManager* manager = [CBLManager sharedInstance];
    CBLListener *listener = [CBLListener alloc];
    [listener initWithManager: manager port: 0];
    //listener.readOnly = YES;  // Do this to prevent writes to your databases
    //listener.passwords = @{@"naomi": @"letmein"};
    NSError* error;
    [listener start:&error];
    if ([listener start:&error])
    {
        if (error) {
            NSLog(@"%@", error.localizedDescription);
            return NO;
        }
        NSLog(@"%@", manager.internalURL);
        UInt16 actualPort = listener.port;  // the actual TCP port it's listening on
        NSLog(@"%hu", actualPort);
    }
    return NO;

The following line throws an error:

[listener start:&error];

I receive the following error:

2016-05-20 13:18:23.617 CouchbaseEvents[18564:934579] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Class CBLListener forgot to implement abstract method start:'

When I look at the Couchbase GitHub repo, the CBLListener.m does not implement ‘start’. However, CBLHTTPListener, for example, extends CBLListener and implements start.

The issue is that the CBLHTTListener.h & all of its dependency headers, are not included in the CouchbaseLiteListener.Framework.

Can someone please let me know how I can get a Framework that has this method implemented?

#2

The problem is this:

CBLListener *listener = [CBLListener alloc];
[listener initWithManager: manager port: 0];

That’s incorrect for any Objective-C class; you’d probably run into similar problems if you did it with NSString, for example. In general, an -init... method is allowed to, and often does, return a different instance than the one alloced. In this case it returns an instance of one of the two concrete subclasses of CBLListener. But your code never assigned that object back to the listener variable.

The correct call looks like:

CBLListener *listener = [[CBLListener alloc] initWithManager: manager port: 0];

or if you want to keep it as two lines, you could do

CBLListener *listener = [CBLListener alloc];
listener = [listener initWithManager: manager port: 0];
#3

Awesome, thank you Jens! I will give this a shot and let you know.

You may want to consider updating the Guide on your Wiki here: https://github.com/couchbase/couchbase-lite-ios/wiki/Guide:-REST

The implementation is written as:

#import <CouchbaseLiteListener/CBLListener.h>

CBLManager* manager = [CBLManager sharedInstance];
_listener = [CBLListener alloc];
[_listener initWithManager: manager port: 0];
_listener.readOnly = YES;  // Do this to prevent writes to your databases
_listener.passwords = @{@"naomi": @"letmein"};
NSError* error;
if ([_listener start:&error])
{
    if (error) {
        NSLog(@"%@", error.localizedDescription);
        return;
    }
    NSLog(@"%@", manager.internalURL);
    UInt16 actualPort = _listener.port;  // the actual TCP port it's listening on
}
#4

Thanks for letting me know; I’ve fixed it. Looks like that code sample was broken by some random Github user three years ago, in an attempt to make the lines shorter. Sigh.

#5

Have you seen any issues with the listener losing connections when the iPhone goes to sleep?

#6

iOS will kill open TCP listener sockets when an app is backgrounded. If you’re using the listener in an iOS app, you’ll need to stop it when the app is backgrounded and restart it when it foregrounds again.

1 Like
#7

Thanks Jens, that’s the conclusion I was coming to also.

#8

I tried stopping the listener but it hasn’t worked. I noticed that after calling stop and then start on the listener that start was failing with the message Address already in use. I took a look at the code for CBLListener.m and saw this:

- (void) stop { /* no-op*/ }

So it doesn’t look like it can be stopped!

Although it probably doesn’t matter. It seems that the behaviour on a simulator is different to a device. The stop method isn’t actually needed because as you say, the OS kills the listener. This doesn’t happen on a simulator, so restarting the listener when the app resumes results in an Address already in use exception.

#9

So it doesn’t look like it can be stopped!

CBLListener is an abstract superclass. The actual implementation of -stop is in CBLHTTPListener.

after calling stop and then start on the listener that start was failing with the message Address already in use

Hm. By default the kernel won’t allow a new listener socket to be opened on a port that was just closed, to avoid confusion. But there’s a flag to override that, and I thought we were setting it.

Anyway, we’re having a parallel discussion on Github issues, and over there I said you probably shouldn’t use the listener; instead use the internalURL of the CBLManager and just point your JS code at that.

1 Like