Synchronizing without internet - Multipeer Connectivity (iOS)

Post on 05-Jul-2015

597 views 3 download

description

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

Transcript of Synchronizing without internet - Multipeer Connectivity (iOS)

Synchronizingwithout internetCodemotion 2014, Madrid

Who am I?Jorge Maroto (@patoroco)

http://maroto.me

· iOS Developer @ticketeaeng

· Playing with iOS since 2010

· Fanboy

Synchronize

¿Internet?

LAN (wired / wi-fi)

What about iOS?

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

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

MultipeerConnectivity

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'.

MultipeerConnectivity.frameworkBonjour | CFNetwork

Two phases· Discovery· Session

Classes#import <MultipeerConnectivity/MultipeerConnectivity.h>

Session infoDiscovery

Advertisement

Session info· MCPeerId· MCSession

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

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

session.delegate = self;

<MCSessionDelegate>- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{ switch (state): { case MCSessionStateConnected: ... case MCSessionStateConnecting: ... case MCSessionStateNotConnected: ... }}

<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{}

Discovery· MCBrowserViewController· MCNearbyServiceBrowser

MCBrowserViewControllerNSString * const serviceIdentifier = @"codemotion-demo";

MCBrowserViewController *browser = [[MCBrowserViewController alloc] initWithServiceType:serviceIdentifier session:self.appdelegate.session];

browser.delegate = self;

[self presentViewController:browser animated:YES completion:nil];

<MCBrowserViewControllerDelegate>- (void)browserViewControllerDidFinish:(MCBrowserViewController *)b {}

- (void)browserViewControllerWasCancelled:(MCBrowserViewController *)b {}

Advertisement· MCAdvertiserAssistant

· MCNearbyServicesAdvertiser

MCAdvertiserAssistantNSString * const serviceIdentifier = @"codemotion-demo";

MCAdvertiserAssistant *advertiser = [[MCAdvertiserAssistant alloc] initWithServiceType:self.serviceIdentifier discoveryInfo:nil session:session];

[advertiser start];

Session phase

Sending data· NSData to an array of peers.

· NSURL resource to a peer.· NSStream to a peer.

Send NSData to an array of peers- (BOOL)sendData:(NSData *)data toPeers:(NSArray *)peerIDs withMode:(MCSessionSendDataMode)mode error:(NSError **)error;

Modes· MCSessionSendDataReliable· MCSessionSendDataUnreliable

DEMOHello world in MC

Video

Send resource to a peer

- (NSProgress *)sendResourceAtURL:(NSURL *)resourceURL withName:(NSString *)resourceName toPeer:(MCPeerID *)peerID withCompletionHandler:(void(^)(NSError *error))completionHandler;

ExampleShare images from photo

library

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

- (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];}

<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];}

<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; });}

VideoShare images from photo

library

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

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]; } ...}

NSOutputStream: write

NSData *data = [NSData data]; [self.output write:data.bytes maxLength:data.length];

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];}

<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; }}

ExampleShared whiteboard

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];}

- (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; }}

- (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();}

VideoShared whiteboard

All together

'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.

'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.

Ensembles1

"Sync for Core Data Apps"1 http://www.ensembles.io

How far can I ...

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.

Questions?

Thanks@patoroco