Search code examples
iosobjective-cmultipeer-connectivity

Multipeer Connection issue with ipv6 don't get an invitation


My App was rejected by Apple because it can't connect to other device running iOS 10.1.1 on Wi-Fi connected to an IPv6 network.
When I tap on connect, the app continues to search for invitees and no further user action is produced.
I use Multi-peer Connectivity and I never tested my App being connected to an IPv6(It's my first release). But the App run very fine without having any connection or being connected to IPv4 network.

I don't know why the App is running and connecting fine using the IPv4 and doesn't connect to peer if the iPad is connected to an IPv6 network.

So my Question: Is it possible to use Multi-peer Connectivity with IPv6 so that Apple can approve the App or how should I handle this Issue ?

Here is my Code, maybe it is something wrong there.

@interface ConnectionManageriOS7 () <MCSessionDelegate, MCBrowserViewControllerDelegate>
{ 
    UILocalNotification *_expireNotification;
    UIBackgroundTaskIdentifier _taskId;
}

@property (nonatomic, strong) MCSession *session;
@property (nonatomic, strong) MCPeerID *localPeerID;

@property (nonatomic, strong) MCBrowserViewController *browserVC;
@property (nonatomic, strong) MCAdvertiserAssistant *advertiser;

@end

static ConnectionManageriOS7 *_manager = nil;

@implementation ConnectionManageriOS7

+ (ConnectionManageriOS7 *)connectManager {

    @synchronized([ConnectionManageriOS7 class]){

        if (_manager == nil) {
            _manager = [[ConnectionManageriOS7 alloc] init];
        }
        return _manager;
    }
    return nil;
}

- (id)init {

    self = [super init];
    if (self) {
        [self setupSessionAndAdvertiser];
    }
    return self;
}

- (void)setupSessionAndAdvertiser {

    _localPeerID = [[MCPeerID alloc] initWithDisplayName:[UIDevice currentDevice].name];;
    _session = [[MCSession alloc] initWithPeer:_localPeerID];
    _session.delegate = self;

}

- (void)connectWithDelegate:(id)delegate {

    _delegate = delegate;
    if (_session.connectedPeers.count) {
        if ([_delegate respondsToSelector:@selector(didConntectedWithManager:)]) {
            [_delegate didConntectedWithManager:self];
        }
    } else {
        if (_advertiser == nil) {
            _advertiser = [[MCAdvertiserAssistant alloc] initWithServiceType:VISUS_Service
                                                               discoveryInfo:nil
                                                                     session:_session];

            _isConnected = NO;

            [_advertiser start];
        }
        if (_browserVC == nil) {

            _browserVC = [[MCBrowserViewController alloc] initWithServiceType:VISUS_Service session:_session];
            _browserVC.delegate = self;

        }

        [(UIViewController *)delegate presentViewController:_browserVC
                                                   animated:YES completion:nil];
    }
}

- (void)sendMessage:(NSString *)message {

    NSData *textData = [message dataUsingEncoding:NSASCIIStringEncoding];
    NSLog(@"Send Data: %@", message);
    NSError *error = nil;
    [_session sendData:textData
               toPeers:_session.connectedPeers
              withMode:MCSessionSendDataReliable
                 error:&error];

    if (error) {
        //        
        [self session:_session peer:nil didChangeState:MCSessionStateNotConnected];
        NSLog(@"error %@", error.userInfo);
    }
}

- (void)stopService {

    NSLog(@"Stop Service");

    [_advertiser stop];
    _advertiser = nil;

    _browserVC = nil;
}

#pragma marks -
#pragma marks MCBrowserViewControllerDelegate
- (void) dismissBrowserVC{
    [_browserVC dismissViewControllerAnimated:YES completion:nil];
}
// Notifies the delegate, when the user taps the done button
- (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController {

    if ([_delegate respondsToSelector:@selector(didConntectedWithManager:)]) {
        [_delegate didConntectedWithManager:self];
    }

    [self dismissBrowserVC];
}

// Notifies delegate that the user taps the cancel button.
- (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController{
    if (_browserVC == nil) {
        [browserViewController dismissViewControllerAnimated:YES completion:nil];
    }else {
        [self dismissBrowserVC];
    }
}

#pragma marks -
#pragma marks MCBrowserViewControllerDelegate

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

    if (state != MCSessionStateConnecting) {
        if (state == MCSessionStateConnected) {

            _isConnected = true;
            if ([_delegate respondsToSelector:@selector(willConntectedWithManager:)]) {
                [_delegate willConntectedWithManager:self];
            }
        }
        else {

            _isConnected = false;
            [self stopService];
            if ([_delegate respondsToSelector:@selector(didDisconntectedWithManager:)]) {
                [_delegate didDisconntectedWithManager:self];
            }
        }

    }

}

// Received data from remote peer
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{
    //  Decode data back to NSString

    NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    NSLog(@"Receive Data: %@", message);

    //  append message to text box:
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([_delegate respondsToSelector:@selector(connectionManager:receivedString:)]) {
            [_delegate connectionManager:self receivedString:message];
        }
    });
}

- (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error {

    _isConnected = false;
    [self stopService];
    NSLog(@"----- Error ----- %@", error.localizedDescription);

}

// Received a byte stream from remote peer
- (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID {

}

// Start receiving a resource from remote peer
- (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress {

}

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


- (void) createExpireNotification
{
    [self killExpireNotification];

    if (_session.connectedPeers.count != 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];
        _expireNotification = n;
        _expireNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:msgTime];
        _expireNotification.alertBody = @"Bluetooth Connectivity is about to disconnect. Open the app to resume Test";
        _expireNotification.soundName = UILocalNotificationDefaultSoundName;
        _expireNotification.applicationIconBadgeNumber = 1;

        [UIApplication.sharedApplication scheduleLocalNotification:_expireNotification];
    }
}

- (void) killExpireNotification
{
    if (_expireNotification != nil)
    {
        [UIApplication.sharedApplication cancelLocalNotification:_expireNotification];
        _expireNotification = nil;
    }
}

- (void)bacgroundHandling {

    _taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^
               {
                   [self stopService];
                   [[UIApplication sharedApplication] endBackgroundTask:_taskId];
                   _taskId = UIBackgroundTaskInvalid;
               }];
    [self createExpireNotification];

}

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

    // http://down.vcnc.co.kr/WWDC_2013/Video/708.pdf  -- wwdc tutorial, this part is towards the end (p119)

   // self.arrayInvitationHandler = [NSArray arrayWithObject:[invitationHandler copy]];
    // ask the user
    UIAlertView *alertView = [[UIAlertView alloc]
                              initWithTitle:peerID.displayName
                              message:@"Would like to create a session with you"
                              delegate:self
                              cancelButtonTitle:@"Decline" otherButtonTitles:@"Accept", nil];
    [alertView show];


}

- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    // retrieve the invitationHandler and  check whether the user accepted or declined the invitation...

    BOOL accept = (buttonIndex != alertView.cancelButtonIndex) ? YES : NO;

    // respond
    if(accept) {
//        void (^invitationHandler)(BOOL, MCSession *) = [self.arrayInvitationHandler objectAtIndex:0];
//        invitationHandler(accept, self.mySession);
    }
    else
    {
        NSLog(@"Session disallowed");
    }
}

- (void)terminate {

    [self killExpireNotification];
    [self stopService];
}
@end

Solution

  • I have solved the problem. For everybody with simular problem:
    It wasn't a problem with IPv6, it is a matter of how to use Multipeer connectivity. In my Case I tryied to the the IPv6 connection with a iPad and a Simulator. And I used my Macbook for creating a nat64 network. And the reason why the simulator and iPad never saw each other the fact that they where not connected to same wifi network.
    Solution:
    Just take for testing two iPads and use your mac as nat64 network accesspoint.