Working With
AFNetworking
@waynehartman
Before AFNetworking
• NSURLConnection (meh)
• NSData (BARF!)
• CFNetwork (medic!)
NSURLConnection - (IBAction)didSelectRefreshButton:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://ip.jsontest.com/"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
!
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
!
#pragma mark - NSURLConnection
!
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
_data = nil;
!
switch (response.statusCode) {
case 200: {
self.data = [NSMutableData dataWithCapacity:(NSUInteger)response.expectedContentLength];
}
break;
default: {
[connection cancel];
!
NSError *error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork
code:response.statusCode
userInfo:@{(NSString *)kCFErrorDescriptionKey : (NSString *)kCFURLErrorFailingURLErrorKey}];
[self connection:connection didFailWithError:error];
}
break;
}
}
!
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.data appendData:data];
}
!
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSError *error = nil;
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:self.data options:0 error:&error];
if (!jsonData) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"The data was corrupt. Sorry"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
} else {
NSString *ip = jsonData[@"ip"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!"
message:[NSString stringWithFormat:@"Your IP is: %@", ip]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
!
self.connection = nil;
}
!
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"There was an error getting the data. Please try again later."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
self.connection = nil;
}
• All this code, just to download
and display this:
{"ip": "72.191.49.142"}
NSData
• One line of code!
!
dataWithContentsOfURL:
!
NEVER USE THIS API!
CFNetwork NSURL *url = [NSURL URLWithString:DOWNLOAD_URL];
!
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)url, kCFHTTPVersion1_1);
!
CFReadStreamRef requestStream = CFReadStreamCreateForHTTPRequest(NULL, request);
CFReadStreamOpen(requestStream);
NSMutableData *responseBytes = [NSMutableData data];
CFIndex numBytesRead = 0 ;
NSUInteger totalBytesRead = 0;
!
do {
UInt8 buf[1024];
numBytesRead = CFReadStreamRead(requestStream, buf, sizeof(buf));
if(numBytesRead > 0) {
[responseBytes appendBytes:buf length:numBytesRead];
totalBytesRead += numBytesRead;
}
} while(numBytesRead > 0);
if (totalBytesRead > 0) {
CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(requestStream, kCFStreamPropertyHTTPResponseHeader);
CFHTTPMessageSetBody(response, (__bridge CFDataRef)responseBytes);
CFReadStreamClose(requestStream);
CFDataRef responseBodyData = CFHTTPMessageCopyBody(response);
NSError *error = nil;
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:(__bridge NSData *)responseBodyData options:0 error:&error];
CFRelease(responseBodyData);
CFRelease(response);
if (!jsonData) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"The data was corrupt. Sorry"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
} else {
NSString *ip = jsonData[@"ip"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!"
message:[NSString stringWithFormat:@"Your IP is: %@", ip]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"There was an error getting the data. Please try again later."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
!
CFRelease(requestStream);
CFRelease(request);
• All this code, just to download
and display this:
{"ip": "72.191.49.142"}
• Plus we have to write gross C
with nasty CFRelease()
AFNetworking
“AFNetworking is a delightful networking library for iOS and Mac OS
X. It's built on top of the Foundation URL Loading System, extending
the powerful high-level networking abstractions built into Cocoa. It
has a modular architecture with well-designed, feature-rich APIs that
are a joy to use.”
AFNetworking
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:DOWNLOAD_URL]];
!
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setResponseSerializer:[AFJSONResponseSerializer serializer]];
[operation setCompletionBlockWithSuccess:successHandler failure:failureHandler];
!
[operation start];
AFNetworking
void(^failureHandler)(AFHTTPRequestOperation *, NSError *) = ^(AFHTTPRequestOperation *operation, NSError *error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"There was an error getting the data. Please try again later."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
};
AFNetworking
void(^successHandler)(AFHTTPRequestOperation *, id) = ^(AFHTTPRequestOperation *operation, id responseObject) {
if (!responseObject) {
failureHandler(operation, [NSError errorWithDomain:AFNetworkingErrorDomain code:404 userInfo:nil]);
} else {
NSString *ip = responseObject[@"ip"];
!
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!"
message:[NSString stringWithFormat:@"Your IP is: %@", ip]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
};
void(^failureHandler)(AFHTTPRequestOperation *, NSError *) = ^(AFHTTPRequestOperation *operation, NSError *error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"There was an error getting the data. Please try again later."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
};
void(^successHandler)(AFHTTPRequestOperation *, id) = ^(AFHTTPRequestOperation *operation, id responseObject) {
if (!responseObject) {
failureHandler(operation, [NSError errorWithDomain:AFNetworkingErrorDomain code:404 userInfo:nil]);
} else {
NSString *ip = responseObject[@"ip"];
!
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!"
message:[NSString stringWithFormat:@"Your IP is: %@", ip]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
};
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:DOWNLOAD_URL]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setResponseSerializer:[AFJSONResponseSerializer serializer]];
[operation setCompletionBlockWithSuccess:successHandler failure:failureHandler];
!
[operation start];
- (IBAction)didSelectRefreshButton:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://ip.jsontest.com/"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
!
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
!
#pragma mark - NSURLConnection
!
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
_data = nil;
!
switch (response.statusCode) {
case 200: {
self.data = [NSMutableData dataWithCapacity:(NSUInteger)response.expectedContentLength];
}
break;
default: {
[connection cancel];
!
NSError *error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork
code:response.statusCode
userInfo:@{(NSString *)kCFErrorDescriptionKey : (NSString *)kCFURLErrorFailingURLErrorKey}];
[self connection:connection didFailWithError:error];
}
break;
}
}
!
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.data appendData:data];
}
!
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSError *error = nil;
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:self.data options:0 error:&error];
if (!jsonData) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"The data was corrupt. Sorry"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
} else {
NSString *ip = jsonData[@"ip"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!"
message:[NSString stringWithFormat:@"Your IP is: %@", ip]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
!
self.connection = nil;
}
!
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"There was an error getting the data. Please try again later."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
self.connection = nil;
}
Key Concepts
• AF*Operation are subclasses of NSOperation
• Networking operations can be started individually or placed in an
NSOperationQueue
• Custom serialization classes can be created for transforming and
validating data.
Demo: Using Queues
AFURLResponseSerializer
• If you don’t specify a request/response serializer, it will give back
NSData.
• Serializers can be created to handle data transformation and
validation.
AFURLResponseSerializer
AFJSONResponseSerializer
AFPropertyListResponseSerializer
AFImageResponseSerializer
AFCompoundResponseSerializer
AFXMLParserResponseSerializer
!
AFXMLDocumentResponseSerializer
Custom Serializer
• Subclass AFHTTPResponseSerializer
• Override
• responseObjectForResponse:data:error:
• acceptableContentTypes
Custom Serializer
• We’re going to create a serializer for decoding 1337593@k
Anyone know the Content-Type for
1337 encoded data?
@99l1c@710n/l337
D3m0: Cu570m R3590n53
53r1@l1z@710n
AFNetworking + UIKit
• AFNetworking adds a number of extensions to common UIKit
classes:
• UIImageView
• UIButton
• UIWebView
• UIProgressView
• UIRefreshControl
Demo: UIKit Extensions
Design Patterns for
Network & Data
“At its heart, programming is all
about abstraction.”
Justin Spahr-Summers
CocoaConf Austin 2014
Classes should have as few purposes as possible (say, one)
and do it very well.
!
Classes should know as little as possible about other classes.
Are your View Controllers like this?
Case Study:
Geekdom for iOS
Monster View Controller
• UITableView Datasource
• Configure Cells
• UITableView Delegate
• Showing Member Details
• NSFetchedResultsControllerDelegate
• Fetch All Member data
• Fetch Checked-In Members
• Fetch Profile Images
• Search Members By Name
• Search Members By Skills
• Data Persistance
• Check-In logic
• Checked-In vs All Members
Questions?
@waynehartman

Working with AFNetworking

  • 1.
  • 2.
    Before AFNetworking • NSURLConnection(meh) • NSData (BARF!) • CFNetwork (medic!)
  • 3.
    NSURLConnection - (IBAction)didSelectRefreshButton:(id)sender{ NSURL *url = [NSURL URLWithString:@"http://ip.jsontest.com/"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; ! self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; } ! #pragma mark - NSURLConnection ! - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response { _data = nil; ! switch (response.statusCode) { case 200: { self.data = [NSMutableData dataWithCapacity:(NSUInteger)response.expectedContentLength]; } break; default: { [connection cancel]; ! NSError *error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:response.statusCode userInfo:@{(NSString *)kCFErrorDescriptionKey : (NSString *)kCFURLErrorFailingURLErrorKey}]; [self connection:connection didFailWithError:error]; } break; } } ! - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [self.data appendData:data]; } ! - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSError *error = nil; NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:self.data options:0 error:&error]; if (!jsonData) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"The data was corrupt. Sorry" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } else { NSString *ip = jsonData[@"ip"]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!" message:[NSString stringWithFormat:@"Your IP is: %@", ip] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } ! self.connection = nil; } ! - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"There was an error getting the data. Please try again later." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; self.connection = nil; } • All this code, just to download and display this: {"ip": "72.191.49.142"}
  • 4.
    NSData • One lineof code! ! dataWithContentsOfURL: ! NEVER USE THIS API!
  • 5.
    CFNetwork NSURL *url= [NSURL URLWithString:DOWNLOAD_URL]; ! CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)url, kCFHTTPVersion1_1); ! CFReadStreamRef requestStream = CFReadStreamCreateForHTTPRequest(NULL, request); CFReadStreamOpen(requestStream); NSMutableData *responseBytes = [NSMutableData data]; CFIndex numBytesRead = 0 ; NSUInteger totalBytesRead = 0; ! do { UInt8 buf[1024]; numBytesRead = CFReadStreamRead(requestStream, buf, sizeof(buf)); if(numBytesRead > 0) { [responseBytes appendBytes:buf length:numBytesRead]; totalBytesRead += numBytesRead; } } while(numBytesRead > 0); if (totalBytesRead > 0) { CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(requestStream, kCFStreamPropertyHTTPResponseHeader); CFHTTPMessageSetBody(response, (__bridge CFDataRef)responseBytes); CFReadStreamClose(requestStream); CFDataRef responseBodyData = CFHTTPMessageCopyBody(response); NSError *error = nil; NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:(__bridge NSData *)responseBodyData options:0 error:&error]; CFRelease(responseBodyData); CFRelease(response); if (!jsonData) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"The data was corrupt. Sorry" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } else { NSString *ip = jsonData[@"ip"]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!" message:[NSString stringWithFormat:@"Your IP is: %@", ip] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } } else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"There was an error getting the data. Please try again later." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } ! CFRelease(requestStream); CFRelease(request); • All this code, just to download and display this: {"ip": "72.191.49.142"} • Plus we have to write gross C with nasty CFRelease()
  • 6.
    AFNetworking “AFNetworking is adelightful networking library for iOS and Mac OS X. It's built on top of the Foundation URL Loading System, extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use.”
  • 7.
    AFNetworking NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:DOWNLOAD_URL]]; ! AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; [operation setResponseSerializer:[AFJSONResponseSerializer serializer]]; [operation setCompletionBlockWithSuccess:successHandler failure:failureHandler]; ! [operation start];
  • 8.
    AFNetworking void(^failureHandler)(AFHTTPRequestOperation *, NSError*) = ^(AFHTTPRequestOperation *operation, NSError *error) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"There was an error getting the data. Please try again later." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; };
  • 9.
    AFNetworking void(^successHandler)(AFHTTPRequestOperation *, id)= ^(AFHTTPRequestOperation *operation, id responseObject) { if (!responseObject) { failureHandler(operation, [NSError errorWithDomain:AFNetworkingErrorDomain code:404 userInfo:nil]); } else { NSString *ip = responseObject[@"ip"]; ! UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!" message:[NSString stringWithFormat:@"Your IP is: %@", ip] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } };
  • 10.
    void(^failureHandler)(AFHTTPRequestOperation *, NSError*) = ^(AFHTTPRequestOperation *operation, NSError *error) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"There was an error getting the data. Please try again later." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; }; void(^successHandler)(AFHTTPRequestOperation *, id) = ^(AFHTTPRequestOperation *operation, id responseObject) { if (!responseObject) { failureHandler(operation, [NSError errorWithDomain:AFNetworkingErrorDomain code:404 userInfo:nil]); } else { NSString *ip = responseObject[@"ip"]; ! UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!" message:[NSString stringWithFormat:@"Your IP is: %@", ip] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } }; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:DOWNLOAD_URL]]; AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; [operation setResponseSerializer:[AFJSONResponseSerializer serializer]]; [operation setCompletionBlockWithSuccess:successHandler failure:failureHandler]; ! [operation start]; - (IBAction)didSelectRefreshButton:(id)sender { NSURL *url = [NSURL URLWithString:@"http://ip.jsontest.com/"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; ! self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; } ! #pragma mark - NSURLConnection ! - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response { _data = nil; ! switch (response.statusCode) { case 200: { self.data = [NSMutableData dataWithCapacity:(NSUInteger)response.expectedContentLength]; } break; default: { [connection cancel]; ! NSError *error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:response.statusCode userInfo:@{(NSString *)kCFErrorDescriptionKey : (NSString *)kCFURLErrorFailingURLErrorKey}]; [self connection:connection didFailWithError:error]; } break; } } ! - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [self.data appendData:data]; } ! - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSError *error = nil; NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:self.data options:0 error:&error]; if (!jsonData) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"The data was corrupt. Sorry" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } else { NSString *ip = jsonData[@"ip"]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!" message:[NSString stringWithFormat:@"Your IP is: %@", ip] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } ! self.connection = nil; } ! - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"There was an error getting the data. Please try again later." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; self.connection = nil; }
  • 12.
    Key Concepts • AF*Operationare subclasses of NSOperation • Networking operations can be started individually or placed in an NSOperationQueue • Custom serialization classes can be created for transforming and validating data.
  • 13.
  • 14.
    AFURLResponseSerializer • If youdon’t specify a request/response serializer, it will give back NSData. • Serializers can be created to handle data transformation and validation.
  • 15.
  • 16.
    Custom Serializer • SubclassAFHTTPResponseSerializer • Override • responseObjectForResponse:data:error: • acceptableContentTypes
  • 17.
    Custom Serializer • We’regoing to create a serializer for decoding 1337593@k
  • 18.
    Anyone know theContent-Type for 1337 encoded data?
  • 19.
  • 20.
  • 21.
    AFNetworking + UIKit •AFNetworking adds a number of extensions to common UIKit classes: • UIImageView • UIButton • UIWebView • UIProgressView • UIRefreshControl
  • 22.
  • 23.
  • 24.
    “At its heart,programming is all about abstraction.” Justin Spahr-Summers CocoaConf Austin 2014
  • 25.
    Classes should haveas few purposes as possible (say, one) and do it very well. ! Classes should know as little as possible about other classes.
  • 26.
    Are your ViewControllers like this?
  • 27.
  • 29.
    Monster View Controller •UITableView Datasource • Configure Cells • UITableView Delegate • Showing Member Details • NSFetchedResultsControllerDelegate • Fetch All Member data • Fetch Checked-In Members • Fetch Profile Images • Search Members By Name • Search Members By Skills • Data Persistance • Check-In logic • Checked-In vs All Members
  • 32.
  • 33.