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. Synchronizing without internet Codemotion 2014, Madrid

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

    @ticketeaeng · Playing with iOS since 2010 · Fanboy
  3. Synchronize

  4. None
  5. None
  6. None
  7. ¿Internet?

  8. LAN (wired / wi-fi)

  9. What about iOS?

  10. 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
  11. 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
  12. Multipeer Connectivity

  13. 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'.
  14. None
  15. None
  16. MultipeerConnectivity.framework Bonjour | CFNetwork

  17. Two phases · Discovery · Session

  18. Classes #import <MultipeerConnectivity/MultipeerConnectivity.h> Session info Discovery Advertisement

  19. Session info · MCPeerId · MCSession

  20. MCPeerId MCPeerID *peerId = [[MCPeerID alloc] initWithDisplayName:self.deviceName];

  21. MCSession MCSession *session = [[MCSession alloc] initWithPeer:self.peerId]; session.delegate = self;

  22. <MCSessionDelegate> - (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state { switch (state):

    { case MCSessionStateConnected: ... case MCSessionStateConnecting: ... case MCSessionStateNotConnected: ... } }
  23. <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{}
  24. Discovery · MCBrowserViewController · MCNearbyServiceBrowser

  25. 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];
  26. None
  27. <MCBrowserViewControllerDelegate> - (void)browserViewControllerDidFinish:(MCBrowserViewController *)b {} - (void)browserViewControllerWasCancelled:(MCBrowserViewController *)b {}

  28. Advertisement · MCAdvertiserAssistant · MCNearbyServicesAdvertiser

  29. MCAdvertiserAssistant NSString * const serviceIdentifier = @"codemotion-demo"; MCAdvertiserAssistant *advertiser =

    [[MCAdvertiserAssistant alloc] initWithServiceType:self.serviceIdentifier discoveryInfo:nil session:session]; [advertiser start];
  30. Session phase

  31. Sending data · NSData to an array of peers. ·

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

    toPeers:(NSArray *)peerIDs withMode:(MCSessionSendDataMode)mode error:(NSError **)error; Modes · MCSessionSendDataReliable · MCSessionSendDataUnreliable
  33. DEMO Hello world in MC

  34. Video

  35. None
  36. Send resource to a peer - (NSProgress *)sendResourceAtURL:(NSURL *)resourceURL withName:(NSString

    *)resourceName toPeer:(MCPeerID *)peerID withCompletionHandler:(void(^)(NSError *error))completionHandler;
  37. Example Share images from photo library

  38. @property (weak, nonatomic) IBOutlet UIImageView *picture;

  39. - (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]; }
  40. <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]; }
  41. <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; }); }
  42. Video Share images from photo library

  43. None
  44. Streaming - (NSOutputStream *)startStreamWithName:(NSString *)streamName toPeer:(MCPeerID *)peerID error:(NSError **)error;

  45. 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]; } ... }
  46. NSOutputStream: write NSData *data = [NSData data]; [self.output write:data.bytes maxLength:data.length];

  47. 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]; }
  48. <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; } }
  49. Example Shared whiteboard

  50. None
  51. 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]; }
  52. - (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; } }
  53. - (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(); }
  54. Video Shared whiteboard

  55. None
  56. All together

  57. '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.
  58. '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.
  59. None
  60. Ensembles1 "Sync for Core Data Apps" 1 http://www.ensembles.io

  61. How far can I ...

  62. 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.
  63. Questions ?

  64. Thanks @patoroco