Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Synchronizing without internet - Multipeer Connectivity (iOS)

Jorge Maroto
November 21, 2014

Synchronizing without internet - Multipeer Connectivity (iOS)

Slides of my presentation in Codemotion 2014, Madrid talking about Multipeer connectivity and how to sync data between devices without need an internet connection or without to create an infraestructure network.

See materials in: https://github.com/patoroco/Codemotion-2014-Multipeer-Connectivity

Jorge Maroto

November 21, 2014
Tweet

More Decks by Jorge Maroto

Other Decks in Programming

Transcript

  1. Who am I? Jorge Maroto (@patoroco) http://maroto.me · iOS Developer

    @ticketeaeng · Playing with iOS since 2010 · Fanboy
  2. Changelog · iOS 3: Game Kit · iOS 4: Game

    Center · iOS 5: Core Bluetooth · iOS 6: Core Bluetooth advertising · iOS 7: Multipeer Connectivity & iBeacons · iOS 8: Handoff
  3. Changelog · iOS 3: Game Kit · iOS 4: Game

    Center · iOS 5: Core Bluetooth · iOS 6: Core Bluetooth advertising · iOS 7: Multipeer Connectivity & iBeacons · iOS 8: Handoff
  4. Multipeer connectivity · Appears in iOS7. · Ability to connect

    to a mesh of peers. · Able to connect to peers over WiFi, ad- hoc wireless, and Bluetooth. · Doesn't require server infraestructure. · Peers must be 'nearby'.
  5. <MCSessionDelegate> - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state { switch (state):

    { case MCSessionStateConnected: ... case MCSessionStateConnecting: ... case MCSessionStateNotConnected: ... } }
  6. <MCSessionDelegate> // DATA - (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID

    {} // RESOURCES - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress {} - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error{} // STREAMS - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID{}
  7. MCBrowserViewController NSString * const serviceIdentifier = @"codemotion-demo"; MCBrowserViewController *browser =

    [[MCBrowserViewController alloc] initWithServiceType:serviceIdentifier session:self.appdelegate.session]; browser.delegate = self; [self presentViewController:browser animated:YES completion:nil];
  8. MCAdvertiserAssistant NSString * const serviceIdentifier = @"codemotion-demo"; MCAdvertiserAssistant *advertiser =

    [[MCAdvertiserAssistant alloc] initWithServiceType:self.serviceIdentifier discoveryInfo:nil session:session]; [advertiser start];
  9. Sending data · NSData to an array of peers. ·

    NSURL resource to a peer. · NSStream to a peer.
  10. Send NSData to an array of peers - (BOOL)sendData:(NSData *)data

    toPeers:(NSArray *)peerIDs withMode:(MCSessionSendDataMode)mode error:(NSError **)error; Modes · MCSessionSendDataReliable · MCSessionSendDataUnreliable
  11. Send resource to a peer - (NSProgress *)sendResourceAtURL:(NSURL *)resourceURL withName:(NSString

    *)resourceName toPeer:(MCPeerID *)peerID withCompletionHandler:(void(^)(NSError *error))completionHandler;
  12. - (IBAction)choosePhoto:(UIButton *)sender { UIImagePickerController *picker = [[UIImagePickerController alloc] init];

    picker.delegate = self; picker.allowsEditing = NO; picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; picker.mediaTypes = @[(NSString *)kUTTypeImage]; [self presentViewController:picker animated:YES completion:nil]; }
  13. <UIImagePickerDelegate> #pragma mark - UIImagePickerDelegate - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info

    { UIImage *selectedImage = info[UIImagePickerControllerOriginalImage]; NSData *jpegImg = UIImageJPEGRepresentation(selectedImage, 0.5); NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingString:@"pic.jpg"]; self.imageURL = [NSURL fileURLWithPath:tmpPath]; [jpegImg writeToURL:self.imageURL atomically:NO]; self.picture.image = selectedImage; [picker dismissViewControllerAnimated:YES completion:nil]; } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissViewControllerAnimated:YES completion:NULL]; }
  14. <MCSessionDelegate> #pragma mark - - (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID

    *)peerID withProgress:(NSProgress *)progress { dispatch_async(dispatch_get_main_queue(), ^{ self.picture.image = nil; self.picture.backgroundColor = [UIColor yellowColor]; }); } - (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error { NSData *data = [NSData dataWithContentsOfURL:localURL]; UIImage *image = [[UIImage alloc] initWithData:data]; dispatch_async(dispatch_get_main_queue(), ^{ self.picture.image = image; }); }
  15. NSOutputStream: open - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state { ...

    if (state == MCSessionStateConnected) { NSError *error; NSOutputStream *output = [session startStreamWithName:@"streamName" toPeer:peer error:&error]; if (error) { return; } [output scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; [output open]; } ... }
  16. NSInputStream - (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID

    { stream.delegate = self; [stream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; [stream open]; }
  17. <NSStreamDelegate> - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { switch (eventCode) { case

    NSStreamEventOpenCompleted: ... case NSStreamEventEndEncountered: ... case NSStreamEventHasBytesAvailable: { NSInputStream *inputStream = (NSInputStream *)aStream; uint8_t buffer[1024]; NSInteger size = [inputStream read:(uint8_t *)buffer maxLength:1024]; NSData *data = [NSData dataWithBytes:buffer length:size]; ... // Manage data received ... break; } default: break; } }
  18. UIPanGestureRecognizer - (IBAction)panReceived:(UIPanGestureRecognizer *)sender { CGPoint point = [sender locationInView:sender.view];

    [self.drawable drawPoint:point state:sender.state]; NSData *data = [NSData drawDataWithGestureState:sender.state point:[sender locationInView:sender.view]]; [self.output write:data.bytes maxLength:data.length]; }
  19. - (void)drawPoint:(CGPoint)point state:(UIGestureRecognizerState)state { switch (state) { case UIGestureRecognizerStateBegan: {

    lastPoint = point; break; } case UIGestureRecognizerStateChanged: { CGPoint currentPoint = point; UIGraphicsBeginImageContext(self.layout.frame.size); [self.tmpLayout.image drawInRect:CGRectMake(0, 0, self.layout.frame.size.width, self.layout.frame.size.height)]; CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y); CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y); CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound); CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 10); CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0, 0, 0, 1.0); CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal); CGContextStrokePath(UIGraphicsGetCurrentContext()); self.tmpLayout.image = UIGraphicsGetImageFromCurrentImageContext(); [self.tmpLayout setAlpha:1]; UIGraphicsEndImageContext(); lastPoint = currentPoint; break; } case UIGestureRecognizerStateEnded: { UIGraphicsBeginImageContext(self.layout.frame.size); [self.layout.image drawInRect:CGRectMake(0, 0, self.layout.frame.size.width, self.layout.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0]; [self.tmpLayout.image drawInRect:CGRectMake(0, 0, self.layout.frame.size.width, self.layout.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0]; self.layout.image = UIGraphicsGetImageFromCurrentImageContext(); self.tmpLayout.image = nil; UIGraphicsEndImageContext(); break; } default: break; } }
  20. - (void)clearScreen { UIGraphicsBeginImageContext(self.layout.frame.size); CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextSetRGBFillColor(ctx, 255.0,

    255.0, 255.0, 1.0); CGContextFillRect(ctx, CGRectMake(0, 0, self.tmpLayout.frame.size.width, self.tmpLayout.frame.size.height)); self.layout.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); }
  21. 'simple sync' example · n-devices. · Create a hash by

    each register based in time. · First sync in batch using a file. · NSOutputstream to send 'ligth data'. · Off and on send counters & timers using MCSessionSendDataReliable.
  22. 'simple sync' example · n-devices. · Create a hash by

    each register based in time. · First sync in batch using a file. · NSOutputstream to send 'ligth data'. · Off and on send counters & timers using MCSessionSendDataReliable.
  23. How far can I ... - RADIOUS2. - DevRocket3 ...

    3 DeckRocket: https://github.com/jpsim/DeckRocket. 2 RADIOUS: https://itunes.apple.com/nz/app/radious-walkie- talkie/id738480541?mt=8.