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!
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.
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];
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With