Upload
chris-adamson
View
2.733
Download
1
Embed Size (px)
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
Citation preview
iOS Networking:Bonjour, iCloud!
Chris Adamson • @invalidnameCodeMash 2012
Monday, January 16, 12
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
The first thing your network app should
do?
Monday, January 16, 12
Monday, January 16, 12
Do I have network access at all?
Monday, January 16, 12
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
Monday, January 16, 12
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
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
Reachability callbacks
• Networks come and go; your app should handle this
• SCNetworkReachabilitySetCallback()
MyNetworkReachabilityCallBack( SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info);
Monday, January 16, 12
Monday, January 16, 12
http://mattgemmell.com/2011/07/25/network-link-conditioner-in-lion/
Monday, January 16, 12
Assuming the network is working…
Monday, January 16, 12
Say hello to iCloud
Monday, January 16, 12
The iCloud API
•
•
•
•
•
Monday, January 16, 12
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
iCloud set-upEnable iCloud for AppID in provisioning portal
Enable entitlements in .xcodeproj (also creates .entitlements file)
Monday, January 16, 12
Browsing in iCloud
• Compose an NSMetadataQuery (from Spotlight API)
• Include NSMetadataQueryUbiquitousDocumentsScope in search scopes
• Start search (asynchronous!)
Monday, January 16, 12
-(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
// 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
- (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
Monday, January 16, 12
But what about document contents?
Monday, January 16, 12
iCloud documents
• Subclass UIDocument (new in iOS 5)
• Override contentsForType: and loadFromContents:ofType:error:
Monday, January 16, 12
- (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
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
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
Saving changes
[note updateChangeCount:UIDocumentChangeDone]; [note closeWithCompletionHandler:^(BOOL success) { NSLog (@"document close was %@", success ? @"successful" : @"not successful"); }];
Monday, January 16, 12
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
Demo: CloudNotes
Monday, January 16, 12
But what if my data isn’t in iCloud?
Monday, January 16, 12
Networking in Foundation
• NSURL
• Classes that load from URLs
• Property lists
• URL Loading System
Monday, January 16, 12
initWithContentsOfURL:
• Supported by NSString, NSData
• Also NSArray and NSDictionary if contents are a property list
• These calls block!
Monday, January 16, 12
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
But I want to host a server on my iPhone!
Monday, January 16, 12
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
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
Network Discovery
Monday, January 16, 12
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
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
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
GameKit
• Simplified Bonjour over Bluetooth or WiFi
• GKPeerPickerController UI for peer discovery
• APIs allow client-server or peer-to-peer
Monday, January 16, 12
GKSession
• All about the delegate callbacks:
• didReceiveConnectionRequestFromPeer:
• peer:didChangeState:
• receiveData:fromPeer:inSession:
Monday, January 16, 12
Demo (if we are really, really lucky)
Monday, January 16, 12
GameKit voice chat
• Set up through Game Center
• Create with -[GKMatch voiceChatWithName:]
• Does anyone use this?
Monday, January 16, 12
Speaking of media…
Monday, January 16, 12
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
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
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
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
Demo: HLS bandwidth
Monday, January 16, 12
Takeaways
Monday, January 16, 12
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
End of line.
Monday, January 16, 12