Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my MCSession peer disconnect randomly?

Im using MCNearbyServiceBrowser and MCNearbyServiceAdvertiser to join two peers to a MCSession. I am able to send data between them using MCSession's sendData method. All seems to be working as expected until I randomly (and not due to any event I control) receive a MCSessionStateNotConnected via the session's MCSessionDelegate didChangeState handler. Additionally, the MCSession's connectedPeers array no longer has my peers.

Two questions: Why? and How do i keep the MCSession from disconnecting?

like image 901
tillerstarr Avatar asked Sep 21 '13 17:09

tillerstarr


2 Answers

This is a bug, which I just reported to Apple. The docs claim the didReceiveCertificate callback is optional, but it's not. Add this method to your MCSessionDelegate:

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

The random disconnects should cease.

like image 124
Andrew Cone Avatar answered Oct 05 '22 20:10

Andrew Cone


UPDATE After using a support ticket to Apple, they confirmed that calling sendData too often and with too much data can cause disconnects.

I have had disconnects when hitting break points and when backgrounding. Since the break points won't happen on the app store, you need to handle the backgrounding case by beginning a background task when your app is about to enter the background. Then end this task when your app comes back to the foreground. On iOS 7 this gives you about 3 background minutes which is better than nothing.

An additional strategy would be to schedule a local notification for maybe 15 seconds before your background time expires by using [[UIApplication sharedApplication] backgroundTimeRemaining], that way you can bring the user back into the app before it suspends and the multi peer framework has to be shutdown. Perhaps the local notification would warn them that their session will expire in 10 seconds or something...

If the background task expires and the app is still in the background, you have to tear down everything related to multi-peer connectivity, otherwise you will get crashes.

- (void) createExpireNotification {     [self killExpireNotification];      if (self.connectedPeerCount != 0) // if peers connected, setup kill switch     {         NSTimeInterval gracePeriod = 20.0f;          // create notification that will get the user back into the app when the background process time is about to expire         NSTimeInterval msgTime = UIApplication.sharedApplication.backgroundTimeRemaining - gracePeriod;         UILocalNotification* n = [[UILocalNotification alloc] init];         self.expireNotification = n;         self.expireNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:msgTime];         self.expireNotification.alertBody = TR(@"Text_MultiPeerIsAboutToExpire");         self.expireNotification.soundName = UILocalNotificationDefaultSoundName;         self.expireNotification.applicationIconBadgeNumber = 1;          [UIApplication.sharedApplication scheduleLocalNotification:self.expireNotification];     } }  - (void) killExpireNotification {     if (self.expireNotification != nil)     {         [UIApplication.sharedApplication cancelLocalNotification:self.expireNotification];         self.expireNotification = nil;     } }  - (void) applicationWillEnterBackground {     self.taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^     {         [self shutdownMultiPeerStuff];         [[UIApplication sharedApplication] endBackgroundTask:self.taskId];         self.taskId = UIBackgroundTaskInvalid;     }];     [self createExpireNotification]; }  - (void) applicationWillEnterForeground {     [self killExpireNotification];     if (self.taskId != UIBackgroundTaskInvalid)     {         [[UIApplication sharedApplication] endBackgroundTask:self.taskId];         self.taskId = UIBackgroundTaskInvalid;     } }  - (void) applicationWillTerminate {     [self killExpireNotification];     [self stop]; // shutdown multi-peer } 

You'll also want this handler in your MCSession delegate due to Apple bug:

- (void) session:(MCSession*)session didReceiveCertificate:(NSArray*)certificate fromPeer:(MCPeerID*)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler  {      if (certificateHandler != nil) { certificateHandler(YES); }  } 
like image 36
jjxtra Avatar answered Oct 05 '22 19:10

jjxtra