Bonjour, iCloud

54
iOS Networking: Bonjour, iCloud! Chris Adamson • @invalidname CodeMash 2012 Monday, January 16, 12

description

Mobile devices are so useful because they can get on the net with their built-in wi-fi or cellular data radios. But how does this work? In iOS, we have a slew of networking APIs, each appropriate in different situations. From decades-old BSD sockets to the new-in-iOS-5 iCloud, there are a wide range of networking calls available to your app, and an equally wide range of semantics in how to use them. In this talk, we'll start with high-level abstractions like iCloud and other objects that can either load from or save to a URL, then progress down through the stack, grabbing arbitrary content from URLs, self-networking with Bonjour and Game Kit, and finally accessing the socket layer with CFNetwork and BSD sockets. iCloud sample code at: http://dl.dropbox.com/u/12216224/conferences/codemash12/bonjour-icloud/CloudNotes.zip

Transcript of Bonjour, iCloud

Page 1: Bonjour, iCloud

iOS Networking:Bonjour, iCloud!

Chris Adamson • @invalidnameCodeMash 2012

Monday, January 16, 12

Page 2: Bonjour, iCloud

What we’ll cover

• Obvious networking APIs

• iCloud, Bonjour, GameKit, CFNetwork

• Not-so-obvious networking APIs

• Foundation, media APIs, System Configuration

Monday, January 16, 12

Page 3: Bonjour, iCloud

The first thing your network app should

do?

Monday, January 16, 12

Page 4: Bonjour, iCloud

Monday, January 16, 12

Page 5: Bonjour, iCloud

Do I have network access at all?

Monday, January 16, 12

Page 6: Bonjour, iCloud

Reachability

• Defined in SystemConfiguration.framework

• C-based API, Obj-C wrapper available as sample code from Apple

• Your network-based app will be rejected if it turtles without network access

Monday, January 16, 12

Page 7: Bonjour, iCloud

Monday, January 16, 12

Page 8: Bonjour, iCloud

Reachability

• Create a SCNetworkReachabilityRef from host name (as a C string) or address (as a sockaddr)

• Get network info with SCNetworkReachabilityGetFlags()

Monday, January 16, 12

Page 9: Bonjour, iCloud

Reachability Flags

enum { kSCNetworkReachabilityFlagsTransientConnection = 1<<0, kSCNetworkReachabilityFlagsReachable = 1<<1, kSCNetworkReachabilityFlagsConnectionRequired = 1<<2, kSCNetworkReachabilityFlagsConnectionOnTraffic = 1<<3, kSCNetworkReachabilityFlagsInterventionRequired = 1<<4, kSCNetworkReachabilityFlagsConnectionOnDemand = 1<<5, kSCNetworkReachabilityFlagsIsLocalAddress = 1<<16, kSCNetworkReachabilityFlagsIsDirect = 1<<17, kSCNetworkReachabilityFlagsIsWWAN = 1<<18, kSCNetworkReachabilityFlagsConnectionAutomatic =

kSCNetworkReachabilityFlagsConnectionOnTraffic};typedef uint32_t SCNetworkReachabilityFlags;

Monday, January 16, 12

Page 10: Bonjour, iCloud

Reachability callbacks

• Networks come and go; your app should handle this

• SCNetworkReachabilitySetCallback()

MyNetworkReachabilityCallBack( SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info);

Monday, January 16, 12

Page 11: Bonjour, iCloud

Monday, January 16, 12

Page 13: Bonjour, iCloud

Assuming the network is working…

Monday, January 16, 12

Page 14: Bonjour, iCloud

Say hello to iCloud

Monday, January 16, 12

Page 15: Bonjour, iCloud

The iCloud API

Monday, January 16, 12

Page 16: Bonjour, iCloud

iCloud: UIDocument

• iCloud does not have its own API, per se

• Special case of saving documents

• Same code to save to local or cloud documents directory by URL

• There are also iCloud APIs for simple key-value storage, and Core Data persistent stores

Monday, January 16, 12

Page 17: Bonjour, iCloud

iCloud set-upEnable iCloud for AppID in provisioning portal

Enable entitlements in .xcodeproj (also creates .entitlements file)

Monday, January 16, 12

Page 18: Bonjour, iCloud

Browsing in iCloud

• Compose an NSMetadataQuery (from Spotlight API)

• Include NSMetadataQueryUbiquitousDocumentsScope in search scopes

• Start search (asynchronous!)

Monday, January 16, 12

Page 19: Bonjour, iCloud

-(void) refreshNotes { NSLog (@"refreshNotes"); [self.noteDictsArray removeAllObjects]; self.currentQuery = [[NSMetadataQuery alloc] init]; // register for notifications [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidUpdate:) name:NSMetadataQueryDidUpdateNotification object:self.currentQuery]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(initalGatherComplete:) name:NSMetadataQueryDidFinishGatheringNotification object:self.currentQuery];

Monday, January 16, 12

Page 20: Bonjour, iCloud

// Configure the search predicate NSPredicate *searchPredicate; searchPredicate=[NSPredicate predicateWithFormat:@"%K LIKE %@", NSMetadataItemFSNameKey, @"*.cloudnote"]; [self.currentQuery setPredicate:searchPredicate]; // Set the search scope to only search iCloud NSArray *searchScopes; searchScopes=[NSArray arrayWithObjects: NSMetadataQueryUbiquitousDocumentsScope,nil]; [self.currentQuery setSearchScopes:searchScopes];

// Start the query! [self.currentQuery startQuery];

Monday, January 16, 12

Page 21: Bonjour, iCloud

- (void)initalGatherComplete:sender; { // Stop the query, the single pass is completed. [self.currentQuery stopQuery]; for (int i=0; i < [self.currentQuery resultCount]; i++) { NSMetadataItem *noteMetadata = [self.currentQuery resultAtIndex:i]; NSString *noteName = [noteMetadata

valueForAttribute:NSMetadataItemDisplayNameKey]; NSURL *noteURL = [noteMetadata valueForAttribute:NSMetadataItemURLKey]; NSDate *noteModifiedDate = [noteMetadata

valueForAttribute:NSMetadataItemFSContentChangeDateKey]; NSLog(@"%@: %@", noteName, noteURL); NSDictionary *noteDict = [NSDictionary dictionaryWithObjectsAndKeys: noteName, NSMetadataItemDisplayNameKey, noteURL, NSMetadataItemURLKey, noteModifiedDate,

NSMetadataItemFSContentChangeDateKey, nil]; [self.noteDictsArray addObject:noteDict]; }

Monday, January 16, 12

Page 22: Bonjour, iCloud

Monday, January 16, 12

Page 23: Bonjour, iCloud

But what about document contents?

Monday, January 16, 12

Page 24: Bonjour, iCloud

iCloud documents

• Subclass UIDocument (new in iOS 5)

• Override contentsForType: and loadFromContents:ofType:error:

Monday, January 16, 12

Page 25: Bonjour, iCloud

- (id)contentsForType:(NSString *)typeName error:(NSError **)outError { NSLog (@"archiving: %@", self.noteDict); return [NSKeyedArchiver archivedDataWithRootObject:self.noteDict];}

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError { BOOL success = NO; if([contents isKindOfClass:[NSData class]] && [contents length] > 0) { NSData *data = (NSData *)contents; self.noteDict = [NSMutableDictionary dictionaryWithDictionary: [NSKeyedUnarchiver unarchiveObjectWithData:data]]; success = YES; } NSLog (@"noteDict: %@", self.noteDict); return success;}

UIDocument overrides

Monday, January 16, 12

Page 26: Bonjour, iCloud

Start a new document in iCloud

NSFileManager *fm = [NSFileManager defaultManager]; NSURL *newDocumentURL = [fm URLForUbiquityContainerIdentifier:nil]; newDocumentURL = [newDocumentURL URLByAppendingPathComponent:@"Documents" isDirectory:YES]; // fileName calculation omitted newDocumentURL = [newDocumentURL URLByAppendingPathComponent:fileName]; CDMNoteDocument *doc = [[CDMNoteDocument alloc] initWithFileURL:newDocumentURL]; // Save the new document to disk. [doc saveToURL:newDocumentURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { NSLog (@"new document %@ saved", success ? @"was" : @"was not"); }];

Monday, January 16, 12

Page 27: Bonjour, iCloud

Open an existing iCloud document

NSURL *noteURL = (NSURL*) [noteDict valueForKey:NSMetadataItemURLKey]; CDMNoteDocument *doc = [[CDMNoteDocument alloc] initWithFileURL:noteURL]; [doc openWithCompletionHandler:^(BOOL success){ NSLog (@"opening doc at %@ %@", doc.fileURL, success ? @"succeeded" : @"failed");

}

Monday, January 16, 12

Page 28: Bonjour, iCloud

Saving changes

[note updateChangeCount:UIDocumentChangeDone]; [note closeWithCompletionHandler:^(BOOL success) { NSLog (@"document close was %@", success ? @"successful" : @"not successful"); }];

Monday, January 16, 12

Page 29: Bonjour, iCloud

Detecting iCloud conflicts

• Register for NSNotificationCenter for UIDocumentStateChangedNotification

• Inspect the documentState property for the value UIDocumentStateInConflict

• Use NSFileVersion method unresolvedConflictVersionsOfItemAtURL to inspect the versions

Monday, January 16, 12

Page 30: Bonjour, iCloud

Demo: CloudNotes

Monday, January 16, 12

Page 31: Bonjour, iCloud

But what if my data isn’t in iCloud?

Monday, January 16, 12

Page 32: Bonjour, iCloud

Networking in Foundation

• NSURL

• Classes that load from URLs

• Property lists

• URL Loading System

Monday, January 16, 12

Page 33: Bonjour, iCloud

initWithContentsOfURL:

• Supported by NSString, NSData

• Also NSArray and NSDictionary if contents are a property list

• These calls block!

Monday, January 16, 12

Page 34: Bonjour, iCloud

URL Loading System

• NSURL + NSURLRequest + NSURLConnection + delegate callbacks

• connection:didReceiveResponse:, connection:didReceiveData:, etc.

• Fairly deep HTTP support (incl. authentication)

• Not an arbitrary socket networking API

Monday, January 16, 12

Page 35: Bonjour, iCloud

But I want to host a server on my iPhone!

Monday, January 16, 12

Page 36: Bonjour, iCloud

CFNetwork

• C-based API, abstractions over network protocols and BSD sockets

• Deep support for HTTP, HTTPS, FTP, DNS

• Built to work asynchronously in RunLoop-based applications (incl. Cocoa Touch)

Monday, January 16, 12

Page 37: Bonjour, iCloud

If you must run a server

• Create CFSocketRef with CFSocketCreate() [or similar] setting callbackTypes to kCFSocketAcceptCallback

• Callback receives a CFSocketNativeHandle

• From this, CFStreamCreatePairWithSocket() to get CFReadStreamRef and CFWriteStreamRef

Monday, January 16, 12

Page 38: Bonjour, iCloud

Network Discovery

Monday, January 16, 12

Page 39: Bonjour, iCloud

Bonjour!

• Protocol for address self-assignment and service discovery

• Published ZEROCONF standard

• Open-source implementations for Mac, Windows, Linux, Java

• APIs: NSNetService and CFNetServiceRef

Monday, January 16, 12

Page 40: Bonjour, iCloud

Searching for Bonjour http servers

-(void) startSearchingForWebServers {! NSNetServiceBrowser bonjourBrowser = [[NSNetServiceBrowser alloc] init];! [bonjourBrowser setDelegate: self];! [bonjourBrowser searchForServicesOfType:@"_http._tcp" inDomain:@""];}

- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowserdidFindService:(NSNetService *)netService moreComing:(BOOL)moreServicesComing {

! [discoveredWebServices addObject: netService];! [tableView reloadData];! if (! moreServicesComing)! ! [activityIndicator stopAnimating];}

Monday, January 16, 12

Page 41: Bonjour, iCloud

Got a service,now what?

• Bonjour provides the discovery, not the service itself

• The NSNetService object includes addresses, host name, and port

• Client connects to the host via NSURLConnection, CFNetwork, etc.

Monday, January 16, 12

Page 42: Bonjour, iCloud

GameKit

• Simplified Bonjour over Bluetooth or WiFi

• GKPeerPickerController UI for peer discovery

• APIs allow client-server or peer-to-peer

Monday, January 16, 12

Page 43: Bonjour, iCloud

GKSession

• All about the delegate callbacks:

• didReceiveConnectionRequestFromPeer:

• peer:didChangeState:

• receiveData:fromPeer:inSession:

Monday, January 16, 12

Page 44: Bonjour, iCloud

Demo (if we are really, really lucky)

Monday, January 16, 12

Page 45: Bonjour, iCloud

GameKit voice chat

• Set up through Game Center

• Create with -[GKMatch voiceChatWithName:]

• Does anyone use this?

Monday, January 16, 12

Page 46: Bonjour, iCloud

Speaking of media…

Monday, January 16, 12

Page 47: Bonjour, iCloud

HTTP Live Streaming

• Firewall-friendly audio/video streaming for iOS and Mac OS X 10.6+ (replaces RTP/RTSP)

• Required for any app that streams more than 10MB over mobile network

• Client support: AVPlayer, MPMoviePlayerController

Monday, January 16, 12

Page 48: Bonjour, iCloud

HLS: How It Works

• Segmenting server splits source media into separate files (usually .m4a for audio-only, .ts for A/V), usually 10 seconds each, and creates an .m3u8 playlist file

• Playlist may point to bandwidth-appropriate playlists

• Clients continually reload playlist, fetch the segments, queue them up

Monday, January 16, 12

Page 49: Bonjour, iCloud

Sample playlist#EXTM3U#EXT-X-TARGETDURATION:8#EXT-X-MEDIA-SEQUENCE:0#EXTINF:8,0640/0640_090110_120505_0.ts#EXTINF:8,0640/0640_090110_120505_1.ts#EXTINF:8,0640/0640_090110_120505_2.ts#EXTINF:8,0640/0640_090110_120505_3.ts#EXTINF:8,0640/0640_090110_120505_4.ts

Monday, January 16, 12

Page 50: Bonjour, iCloud

Bandwidth-delimited playlist

#EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000 http://example.com/low.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000 http://example.com/mid.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000 http://example.com/hi.m3u8

Monday, January 16, 12

Page 51: Bonjour, iCloud

Demo: HLS bandwidth

Monday, January 16, 12

Page 52: Bonjour, iCloud

Takeaways

Monday, January 16, 12

Page 53: Bonjour, iCloud

iOS Priorities

• Asynchronicity — don’t block the UI

• Many network APIs are difficult or impossible to block on

• Domain-appropriate abstractions

• iCloud, GameKit, HLS

• You don’t need to know the difference between cellular and wifi

Monday, January 16, 12

Page 54: Bonjour, iCloud

End of line.

Monday, January 16, 12