Post on 05-Jul-2015
description
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.
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