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];
}
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 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.