Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multipeer Connectivity: getting an invitation accepted (using built-in browser VC)

I'm trying to follow the WWDC talk to learn about the MultipeerConnectivity framework. After many false starts, the browser(s) show the peers, and invitations get issued.

But when I press "Accept" on the peer device, the browser keeps showing "Connecting" without end. I thought that the MCBrowserViewController took care of the logic and I could relax until the browser's user pressed Cancel or Done, and the delegate method fired. I bet it's something obvious, but it's eluding me.

Here's what I hope is the relevant code. I have it in the AppDelegate. NSLog statements in the various delegate methods get called as I would expect—except for the one in browserViewControllerDidFinish: of course.

Bear in mind that the browser and invitations do appear, so something is right!

In the @interface...

@property   (strong, nonatomic) MCSession   *theSession;
@property   (strong, nonatomic) MCAdvertiserAssistant       *assistant;
@property   (strong, nonatomic) MCBrowserViewController     *browserVC;

In the @implementation

static    NSString* const    kServiceType = @"eeps-multi";

// called from viewDidAppear in the main ViewController

-(void)     startSession
{
    if (!self.theSession) {
        UIDevice *thisDevice = [UIDevice currentDevice];

        MCPeerID *aPeerID = [[ MCPeerID alloc ] initWithDisplayName: thisDevice.name];
        self.theSession = [[ MCSession alloc ] initWithPeer: aPeerID ];
        self.theSession.delegate = self;
    } else {
        NSLog(@"Session init skipped -- already exists");
    }
}

// called from viewDidAppear in the main ViewController

- (void)    startAdvertising
    {
    if (!self.assistant) {
        self.assistant = [[MCAdvertiserAssistant alloc] initWithServiceType:kServiceType
                                                              discoveryInfo:nil
                                                                    session:self.theSession ];
        self.assistant.delegate = self;
        [ self.assistant start ];
    } else {
        NSLog(@"Advertiser init skipped -- already exists");
    }
}

// called from the main ViewController in response to a button press

- (void)    startBrowsing
{
    if (!self.browserVC){
        self.browserVC = [[MCBrowserViewController alloc] initWithServiceType:kServiceType 
                                                                      session:self.theSession];
        self.browserVC.delegate = self;
    } else {
        NSLog(@"Browser VC init skipped -- already exists");
    }

    [ self.window.rootViewController presentViewController:self.browserVC animated:YES completion:nil];
}

Thanks in advance!

like image 874
Tim Erickson Avatar asked Sep 26 '13 00:09

Tim Erickson


3 Answers

Thanks to the commenters for excellent suggestions that led to my finding my own mistake. And here it is:

If you implement the MCSessionDelegate method session:didReceiveCertificate:fromPeer:certificateHandler method, it will intercept the peer's attempt to connect to the session. You should either explicitly approve that connection in that method or comment it out.

Details and lessons learned:

In addition to the code I showed, I had made stubby implementations of the various delegate methods. One MCSessionDelegate method is this one:

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

}

Extending @Big-O Claire 's advice above, I started watching all these delegate methods, Just In Case. And this one fired when the peer tapped the Accept button in the AdvertiserAssistant UI.

This method gives you a chance to decide if the peer is legit and not connect (using the certificateHandler:) if you don't want to. Apple says,

Your app should inspect the nearby peer’s certificate, and then should decide whether to trust that certificate. Upon making that determination, your app should call the provided certificateHandler block, passing either YES (to trust the nearby peer) or NO (to reject it).

In addition, you get this tip:

Important: The multipeer connectivity framework makes no attempt to validate the peer-provided identity or certificates in any way. If your delegate does not implement this method, all certificates are accepted automatically.

When I commented this method out, the connections went through — and THIS problem, at least, was solved.

like image 65
Tim Erickson Avatar answered Oct 12 '22 16:10

Tim Erickson


I was having the same issue and it turned out i was using the same session for both the browser and the advertiser. split up the sessions but make sure serviceType is the same and it'll work like a charm

- (void) setUpMultipeer{
    //  Setup Peer ID
    self.myPeerID = [[MCPeerID alloc] initWithDisplayName:[UIDevice currentDevice].name];

    //  Setup Sessions
    self.advertiseSession = [[MCSession alloc] initWithPeer:self.myPeerID];
    self.advertiseSession.delegate = self;

    self.browserSession = [[MCSession alloc] initWithPeer:self.myPeerID];
    self.browserSession.delegate = self;


    //  Setup BrowserVC
    self.browserVC = [[MCBrowserViewController alloc] initWithServiceType:@"SERVICE_TYPE" session:self.browserSession];
    self.browserVC.delegate = self;

    //  Setup Advertiser
    self.advertiser = [[MCAdvertiserAssistant alloc] initWithServiceType:@"SERVICE_TYPE" discoveryInfo:nil session:self.advertiseSession];
    [self.advertiser start];
}
like image 3
Alaa Awad Avatar answered Oct 12 '22 14:10

Alaa Awad


I've not gone the MCBrowserViewController route myself when working with the new MC framework, but from slide 51 of the WWDC presentation, it does look like the browserViewControllerDidFinish: is only called when the user presses done. So this callback is probably not where the problem lies if your peer is still showing up as "Connecting...".

I'm wondering if you have to connect your peers to a session manually. You're already setting the MCSession delegate, so I'm assuming you're implementing session:peer:didChangeState. Set a breakpoint and watch for when MCSessionState is MCSessionStateConnected. The only thing I'm unsure of is if you need to manually handle this on the advertiser side, the browser side, or both. If you could figure out at which step the framework stops handling it, that would be helpful.

like image 1
Big-O Claire Avatar answered Oct 12 '22 14:10

Big-O Claire