Question

I'm using the mutlipeer connectivity framework for the first time, and I want programmatic ( not with the assistant classes) control.

Everything is working exactly as described when I run my code on two separate devices up until the point when the 'advertiser' receives the delegate callback:

The browsing client's delegate callback is called when it discovers the advertiser:

-(void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info{
    [[[UIAlertView alloc] initWithTitle:@"Peer Found" message:peerID.displayName delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show];

    _session = [[MCSession alloc] initWithPeer:_myPeerID];
    _session.delegate = self;

    //connect to the discovered peer.
    [_browser invitePeer:peerID toSession:_session withContext:nil timeout:30.0];
    [_browser stopBrowsingForPeers];

}

Then the advertising client's delegate callback is called when it receives the invite:

-(void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(NSData *)context invitationHandler:(void (^)(BOOL, MCSession *))invitationHandler{

    //when my code runs, everything looks correct here. 
    //eg. peerID is definitely my 'browser' client's display name etc.

    _session = [[MCSession alloc] initWithPeer:_myPeerID];
    _session.delegate = self;

    //using a simple version for testing... accept all invites.
    invitationHandler(YES, _session);

    //stop advertising now.
    [_advertiser stopAdvertisingPeer];
}

After 'invitationHandler(YES, _session)' is called, it seems like the connection is never established between the 'browsing' client and the 'advertising' client.

I don't ever receive any delegate callbacks (once or twice I received a MCSessionStateNotConnected ) on the MCSession objects on either client device. I would have thought I would have received the MCSession delegate callback:

-(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state;

Am I missing something? Has anyone else come across this issue?

Was it helpful?

Solution

There is a bug that Apple is aware of apparently.

This is what led to the discovery: Why does my MCSession peer disconnect randomly?

You must implement the following delegate callback even though it is listed as optional in the docs...

- (void) session:(MCSession *)session didReceiveCertificate:(NSArray *)certificate fromPeer:(MCPeerID *)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler
{
 certificateHandler(YES);
}

OTHER TIPS

The "didReceiveCertificate" delegate method is optional and if you don't implement it, the framework will assume that you accept the certificate (note that certificate can be nil).

However, if you implement the method and then leave it empty, then, sure, the peer will not connect, because the framework will expect you to call the certificateHandler with either a YES or NO.

I've been having similar problems. It seems though that if I have run my app on one iOS device, and connected to another, then quit and relaunch (say when I rerun from Xcode), then I am in a situation where I get a Connected message and then a Not Connected message a little later. This was throwing me off. But looking more carefully, I can see that the Not Connected message is actually meant for a different peerId than the one that has connected.

I think the problem here is that most samples I've seen just care about the displayName of the peerID, and neglect the fact that you can get multiple peerIDs for the same device/displayName.

I am now checking the displayName first and then verifying that the peerID is the same, by doing a compare of the pointers.

- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {

    MyPlayer *player = _players[peerID.displayName];

    if ((state == MCSessionStateNotConnected) &&
        (peerID != player.peerID)) {
        NSLog(@"remnant connection drop");
        return; // note that I don't care if player is nil, since I don't want to
                // add a dictionary object for a Not Connecting peer.
    }
    if (player == nil) {
        player = [MyPlayer init];
        player.peerID = peerID;
        _players[peerID.displayName] = player;
    }
    player.state = state;

...

Another problem I found (also in other sample codes, i.e PeerKit) is that stopAdvertisingPeer right after invitationHandler(YES) is probably wrong. Because even you accept invitation there is no guarantee you will be connected. I think it is better to stopAdvertisingPeer only when is connected.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top