Reconnecting to disconnected peers

James Andrews picture James Andrews · Oct 19, 2013 · Viewed 10.2k times · Source

I'm using the iOS 7 Multipeer framework in my app but I'm experiencing a problem with devices disconnecting. If I open the app in two devices: device A and device B the two devices connect to each other automatically. However, after several seconds device A disconnects from device B. i.e. At first the connection is like this:

A ---> B
A <--- B

After several seconds:

A ---> B
A      B

Device A maintains it's connection but device B get's a MCSessionStateNotConnected.

This means that A can send data to B but B can't reply. I tried to get around this by checking if the device is connected and if it's not, re-initiating the connection using:

[browser invitePeer:peerID toSession:_session withContext:Nil timeout:10];

But the didChangeState callback just get's called with MCSessionStateNotConnected.

Strangely if I send app A to the background, then re-open it, B reconnects to it and the connection is maintained.

The Multipeer API (and documentation) seems a bit sparse so I was assuming that it would just work. In this situation how should I re-connect the device?

Answer

ChrisH picture ChrisH · Oct 23, 2013

I was having the same problem, and it seems to have been related to my app browsing and advertising at the same time, and two invitations being sent/accepted. When I stopped doing this and let one peer defer to the other for invitations the devices stayed connected.

In my browser delegate I'm checking the hash value of the discovered peer's displayName and only sending an invitation if my peer has a higher hash value:

Edit

As pointed out by @Masa the hash value of an NSString will be different on 32 and 64 bit devices, so it's safer to use the compare: method on displayName.

- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info {

    NSLog(@"Browser found peer ID %@",peerID.displayName);       

    //displayName is created with [[NSUUID UUID] UUIDString]

    BOOL shouldInvite = ([_myPeerID.displayName compare:peerID.displayName]==NSOrderedDescending);

    if (shouldInvite){
        [browser invitePeer:peerID toSession:_session withContext:nil timeout:1.0]; 
    }
    else {
        NSLog(@"Not inviting");
    }
}

As you say, the documentation is sparse so who knows what Apple really wants us to do, but I've experimented with both sending and accepting invitations using a single session, and also creating a new session for each invitation accepted/sent, but this particular way of doing things has given me the most success.