Sie sind auf Seite 1von 27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

AFNetworking 2.0 Tutorial


JoshuaGreeneonFebruary17,2014

Update1/18/2014:FullyupdatedforiOS7andAFNetworking2.0
(originalpostbyScottSherwood,updatebyJoshuaGreene).
IniOS7,AppleintroducedNSURLSessionasthenew,preferred
methodofnetworking(asopposedtotheolderNSURLConnection
API).UsingthisrawNSURLSessionAPIisdefinitelyavalidwayto
writeyournetworkingcodeweevenhaveatutorialonthat.
However,theresanalternativetoconsiderusingthepopular
thirdpartynetworkinglibraryAFNetworking.
ThelatestversionofAFNetworking(2.0)isnowbuiltontopof
NSURLSession,soyougetallofthegreatfeaturesprovidedthere.
Butyoualsogetalotofextracoolfeatureslikeserialization,
reachabilitysupport,UIKitintegration(suchasahandycategory
onasynchronouslyloadingimagesinaUIImageView),andmore.
AFNetworkingisincrediblypopularitwonourReadersChoice
2012BestiOSLibraryAward.Itsalsooneofthemostwidelyused,
opensourceprojectswithover10,000stars,2,600forks,and160
contributorsonGithub.

LearnhowtouseAFNetworking:aneasy
tousenetworkAPIforiOS!

InthisAFNetworking2.0tutorial,youwilllearnaboutthemajor
componentsofAFNetworkingbybuildingaWeatherAppthatusesfeedsfromWorldWeatherOnline.Youllstartwith
staticweatherdata,butbytheendofthetutorial,theappwillbefullyconnectedtoliveweatherfeeds.
Todaysforecast:acooldeveloperlearnsallaboutAFNetworkingandgetsinspiredtouseitinhis/herapps.Letsget
busy!

Getting Started
FirstdownloadthestarterprojectforthisAFNetworking2.0tutorialhere.
ThisprojectprovidesabasicUItogetyoustartednoAFNetworkingcodehasbeenaddedyet.
OpenMainStoryboard.storyboard,andyouwillseethreeviewcontrollers:

http://www.raywenderlich.com/59255/afnetworking20tutorial

1/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

Fromlefttoright,theyare:
Atoplevelnavigationcontroller
Atableviewcontrollerthatwilldisplaytheweather,onerowperday
Acustomviewcontroller(WeatherAnimationViewController)thatwillshowtheweatherforasingledaywhenthe
usertapsonatableviewcell
Buildandruntheproject.YoullseetheUIappear,butnothingworksyet.Thatsbecausetheappneedstogetitsdata
fromthenetwork,butthiscodehasntbeenaddedyet.Thisiswhatyouwillbedoinginthistutorial!
ThefirstthingyouneedtodoisincludetheAFNetworkingframeworkinyourproject.Downloadthelatestversionfrom
GitHubbyclickingontheDownloadZiplink.
Whenyouunzipthefile,youwillseethatitincludesseveralsubfoldersanditems.Ofparticularinterest,itincludesa
subfoldercalledAFNetworkingandanothercalledUIKit+AFNetworkingasshownbelow:

http://www.raywenderlich.com/59255/afnetworking20tutorial

2/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

DragthesefoldersintoyourXcodeproject.

http://www.raywenderlich.com/59255/afnetworking20tutorial

3/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

Whenpresentedwithoptionsforaddingthefolders,makesurethatCopyitemsintodestinationgroupsfolder(if
needed)andCreategroupsforanyaddedfoldersarebothchecked.
Tocompletethesetup,opentheprecompiledheaderWeatherPrefix.pchfromtheSupportingFilessectionofthe
project.Addthislineaftertheotherimports:
#import "AFNetworking.h"
AddingAFNetworkingtotheprecompiledheadermeansthattheframeworkwillbeautomaticallyincludedinallthe
projectssourcefiles.
Prettyeasy,eh?Nowyourereadytoweatherthecode!

Operation JSON
AFNetworkingissmartenoughtoloadandprocessstructureddataoverthenetwork,aswellasplainoldHTTPrequests.
Inparticular,itsupportsJSON,XMLandPropertyLists(plists).
YoucoulddownloadsomeJSONandthenrunitthroughaparser(likethebuiltinNSJSONSerialization)yourself,but
whybother?AFNetworkingcandoitall!

http://www.raywenderlich.com/59255/afnetworking20tutorial

4/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

FirstyouneedthebaseURLofthetestscript.AddthistothetopofWTTableViewController.m,justunderneathallthe
#importlines.
static NSString * const BaseURLString =
@"http://www.raywenderlich.com/demos/weather_sample/";
ThisistheURLtoanincrediblysimplewebservicethatIcreatedforyouforthistutorial.Ifyourecuriouswhatitlooks
like,youcandownloadthesource.
ThewebservicereturnsweatherdatainthreedifferentformatsJSON,XML,andPLIST.Youcantakealookatthedata
itcanreturnbyusingtheseURLS:
http://www.raywenderlich.com/demos/weather_sample/weather.php?format=json
http://www.raywenderlich.com/demos/weather_sample/weather.php?format=xml
http://www.raywenderlich.com/demos/weather_sample/weather.php?format=plist(mightnotshowcorrectlyinyour
browser)
ThefirstdataformatyouwillbeusingisJSON.JSONisaverycommonJavaScriptderivedobjectformat.Itlooks
somethinglikethis:
{
"data": {
"current_condition": [
{
"cloudcover": "16",
"humidity": "59",
"observation_time": "09:09 PM",
}
]
}
}
Note:IfyoudliketolearnmoreaboutJSON,checkoutourWorkingwithJSONTutorial.

WhentheusertapstheJSONbutton,theappwillloadandprocessJSONdatafromtheserver.In
WTTableViewController.m,findthejsonTapped:method(itshouldbeempty)andreplaceitwiththefollowing:
- (IBAction)jsonTapped:(id)sender
{
// 1
http://www.raywenderlich.com/59255/afnetworking20tutorial

5/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

NSString *string = [NSString stringWithFormat:@"%@weather.php?format=json",


BaseURLString];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];

// 2
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id


responseObject) {

// 3
self.weather = (NSDictionary *)responseObject;
self.title = @"JSON Retrieved";
[self.tableView reloadData];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

// 4
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving
Weather"
message:[error
localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];
}];

// 5
[operation start];
}
Awesome,thisisyourfirstAFNetworkingcode!Sincethisisallnew,Illexplainitonesectionatatime.
1. YoufirstcreateastringrepresentingthefullurlfromthebaseURLstring.ThisisthenusedtocreateanNSURL
object,whichisusedtomakeanNSURLRequest.
2. AFHTTPRequestOperationisanallinoneclassforhandlingHTTPtransfersacrossthenetwork.Youtellitthatthe
responseshouldbereadasJSONbysettingtheresponseSerializerpropertytothedefaultJSONserializer.
AFNetworkingwillthentakecareofparsingtheJSONforyou.
3. Thesuccessblockrunswhen(surprise!)therequestsucceeds.TheJSONserializerparsesthereceiveddataand
returnsadictionaryintheresponseObjectvariable,whichisstoredintheweatherproperty.
4. Thefailureblockrunsifsomethinggoeswrongsuchasifnetworkingisntavailable.Ifthishappens,yousimply
displayanalertwiththeerrormessage.
5. Youmustexplicitlytelltheoperationtostart(orelsenothingwillhappen).
Asyoucansee,AFNetworkingisextremelysimpletouse.Injustafewlinesofcode,youwereabletocreatea
networkingoperationthatbothdownloadsandparsesitsresponse.
Nowthattheweatherdataisstoredinself.weather,youneedtodisplayit.Findthe
tableView:numberOfRowsInSection:methodandreplaceitwiththefollowing:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(!self.weather)
http://www.raywenderlich.com/59255/afnetworking20tutorial

6/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

return 0;

switch (section) {
case 0: {
return 1;
}
case 1: {
NSArray *upcomingWeather = [self.weather upcomingWeather];
return [upcomingWeather count];
}
default:
return 0;
}
}
Thetableviewwillhavetwosections:thefirsttodisplaythecurrentweatherandthesecondtodisplaytheupcoming
weather.
Waitaminute!,youmightbethinking.Whatisthis[self.weatherupcomingWeather]?Ifself.weatherisaplainold
NSDictionary,howdoesitknowwhatupcomingWeatheris?
Tomakeiteasiertodisplaythedata,IaddedacoupleofhelpercategoriesonNSDictionaryinthestarterproject:
NSDictionary+weather
NSDictionary+weather_package
Thesecategoriesaddsomehandymethodsthatmakeitalittleeasiertoaccessthedataelements.Youwanttofocuson
thenetworkingpartandnotonnavigatingNSDictionarykeys,right?

Note:FYI,analternativewaytomakeworkingwithJSONresultsabiteasierthanlookingupkeysindictionariesor
creatingspecialcategorieslikethisistouseathirdpartylibrarylikeJSONModel.

StillinWTTableViewController.m,findthetableView:cellForRowAtIndexPath:methodandreplaceitwiththefollowing
implementation:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static NSString *CellIdentifier = @"WeatherCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];

NSDictionary *daysWeather = nil;

switch (indexPath.section) {
case 0: {
daysWeather = [self.weather currentCondition];
break;
}

case 1: {
NSArray *upcomingWeather = [self.weather upcomingWeather];
daysWeather = upcomingWeather[indexPath.row];
break;
}

default:
break;
http://www.raywenderlich.com/59255/afnetworking20tutorial

7/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

cell.textLabel.text = [daysWeather weatherDescription];

// You will add code here later to customize the cell, but it's good for now.

return cell;
}
LikethetableView:numberOfRowsInSection:method,thehandyNSDictionarycategoriesareusedtoeasilyaccessthe
data.Thecurrentdaysweatherisadictionary,andtheupcomingdaysarestoredinanarray.
BuildandrunyourprojecttapontheJSONbuttontogetthenetworkingrequestinmotionandyoushouldseethis:

JSONsuccess!

Operation Property Lists


Propertylists(orplistsforshort)arejustXMLfilesstructuredinacertainway(definedbyApple).Appleusesthemall
overtheplaceforthingslikestoringusersettings.Theylooksomethinglikethis:
http://www.raywenderlich.com/59255/afnetworking20tutorial

8/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

<dict>
<key>data</key>
<dict>
<key>current_condition</key>
<array>
<dict>
<key>cloudcover</key>
<string>16</string>
<key>humidity</key>
<string>59</string>
...
Theaboverepresents:
Adictionarywithasinglekeycalleddatathatcontainsanotherdictionary.
Thatdictionaryhasasinglekeycalledcurrent_conditionthatcontainsanarray.
Thatarraycontainsadictionarywithseveralkeysandvalues,likecloudcover=16andhumidity=59.
Itstimetoloadtheplistversionoftheweatherdata.FindtheplistTapped:methodandreplacetheempty
implementationwiththefollowing:
- (IBAction)plistTapped:(id)sender
{
NSString *string = [NSString stringWithFormat:@"%@weather.php?format=plist",
BaseURLString];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]


initWithRequest:request];

// Make sure to set the responseSerializer correctly


operation.responseSerializer = [AFPropertyListResponseSerializer serializer];

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id


responseObject) {

self.weather = (NSDictionary *)responseObject;


self.title = @"PLIST Retrieved";
[self.tableView reloadData];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving


Weather"
message:[error
localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];
}];

[operation start];
}
NoticethatthiscodeisalmostidenticaltotheJSONversion,exceptforchangingtheresponseSerializertothedefault
AFPropertyListResponseSerializertoletAFNetworkingknowthatyouregoingtobeparsingaplist.
http://www.raywenderlich.com/59255/afnetworking20tutorial

9/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

Thatsprettyneat:yourappcanaccepteitherJSONorplistformatswithjustatinychangetothecode!
BuildandrunyourprojectandtrytappingonthePLISTbutton.Youshouldseesomethinglikethis:

TheClearbuttoninthetopnavigationbarwillclearthetitleandtableviewdatasoyoucanreseteverythingtomake
suretherequestsaregoingthrough.

Operation XML
WhileAFNetworkinghandlesJSONandplistparsingforyou,workingwithXMLisalittlemorecomplicated.Thistime,its
yourjobtoconstructtheweatherdictionaryfromtheXMLfeed.
Fortunately,iOSprovidessomehelpviatheNSXMLParserclass(whichisaSAXparser,ifyouwanttoreaduponit).
StillinWTTableViewController.m,findthexmlTapped:methodandreplaceitsimplementationwiththefollowing:
- (IBAction)xmlTapped:(id)sender
{
NSString *string = [NSString stringWithFormat:@"%@weather.php?format=xml",
BaseURLString];
NSURL *url = [NSURL URLWithString:string];
http://www.raywenderlich.com/59255/afnetworking20tutorial

10/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

NSURLRequest *request = [NSURLRequest requestWithURL:url];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]


initWithRequest:request];

// Make sure to set the responseSerializer correctly


operation.responseSerializer = [AFXMLParserResponseSerializer serializer];

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id


responseObject) {

NSXMLParser *XMLParser = (NSXMLParser *)responseObject;


[XMLParser setShouldProcessNamespaces:YES];

// Leave these commented for now (you first need to add the delegate methods)
// XMLParser.delegate = self;
// [XMLParser parse];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving


Weather"
message:[error
localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];

}];

[operation start];
}
Thisshouldlookprettyfamiliarbynow.Thebiggestchangeisthatinthesuccessblockyoudontgetanice,
preprocessedNSDictionaryobjectpassedtoyou.Instead,responseObjectisaninstanceofNSXMLParser,whichyou
willusetodotheheavyliftinginparsingtheXML.
YoullneedtoimplementasetofdelegatemethodsforNXMLParsertobeabletoparsetheXML.Noticethat
XMLParsersdelegateissettoself,soyouwillneedtoaddNSXMLParsersdelegatemethodsto
WTTableViewControllertohandletheparsing.
First,updateWTTableViewController.handchangetheclassdeclarationatthetopasfollows:
@interface WTTableViewController : UITableViewController<NSXMLParserDelegate>
ThismeanstheclasswillimplementtheNSXMLParserDelegateprotocol.Youwillimplementthesemethodssoon,but
firstyouneedtoaddafewproperties.
AddthefollowingpropertiestoWTTableViewController.mwithintheclassextension,rightafter@interface
WTTableViewController():
@property(nonatomic,
being parsed
@property(nonatomic,
xml response
@property(nonatomic,
@property(nonatomic,

strong) NSMutableDictionary *currentDictionary;

// current section

strong) NSMutableDictionary *xmlWeather;

// completed parsed

strong) NSString *elementName;


strong) NSMutableString *outstring;

ThesepropertieswillcomeinhandywhenyoureparsingtheXML.

http://www.raywenderlich.com/59255/afnetworking20tutorial

11/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

NowpastethismethodinWTTableViewController.m,rightbefore@end:
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.xmlWeather = [NSMutableDictionary dictionary];
}
Theparsercallsthismethodwhenitfirststartsparsing.Whenthishappens,yousetself.xmlWeathertoanew
dictionary,whichwillholdholdtheXMLdata.
Nextpastethismethodrightafterthispreviousone:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:
(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary
*)attributeDict
{
self.elementName = qName;

if([qName isEqualToString:@"current_condition"] ||
[qName isEqualToString:@"weather"] ||
[qName isEqualToString:@"request"]) {
self.currentDictionary = [NSMutableDictionary dictionary];
}

self.outstring = [NSMutableString string];


}
Theparsercallsthismethodwhenitfindsanewelementstarttag.Whenthishappens,youkeeptrackofthenew
elementsnameasself.elementNameandthensetself.currentDictionarytoanewdictionaryiftheelementname
representsthestartofanewweatherforecast.Youalsoresetoutstringasanewmutablestringinpreparationfornew
XMLtobereceivedrelatedtotheelement.
Nextpastethismethodjustafterthepreviousone:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (!self.elementName)
return;

[self.outstring appendFormat:@"%@", string];


}
Asthenamesuggests,theparsercallsthismethodwhenitfindsnewcharactersonanXMLelement.Youappendthe
newcharacterstooutstring,sotheycanbeprocessedoncetheXMLtagisclosed.
Again,pastethisnextmethodjustafterthepreviousone:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:
(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// 1
if ([qName isEqualToString:@"current_condition"] ||
[qName isEqualToString:@"request"]) {
self.xmlWeather[qName] = @[self.currentDictionary];
self.currentDictionary = nil;
}
// 2
else if ([qName isEqualToString:@"weather"]) {

// Initialize the list of weather items if it doesn't exist


NSMutableArray *array = self.xmlWeather[@"weather"] ?: [NSMutableArray array];
http://www.raywenderlich.com/59255/afnetworking20tutorial

12/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

// Add the current weather object


[array addObject:self.currentDictionary];

// Set the new array to the "weather" key on xmlWeather dictionary


self.xmlWeather[@"weather"] = array;

self.currentDictionary = nil;
}
// 3
else if ([qName isEqualToString:@"value"]) {
// Ignore value tags, they only appear in the two conditions below
}
// 4
else if ([qName isEqualToString:@"weatherDesc"] ||
[qName isEqualToString:@"weatherIconUrl"]) {
NSDictionary *dictionary = @{@"value": self.outstring};
NSArray *array = @[dictionary];
self.currentDictionary[qName] = array;
}
// 5
else if (qName) {
self.currentDictionary[qName] = self.outstring;
}

self.elementName = nil;
}
Thismethodiscalledwhenanendelementtagisencountered.Whenthathappens,youcheckforafewspecialtags:
1. Thecurrent_conditionelementindicatesyouhavetheweatherforthecurrentday.Youaddthisdirectlytothe
xmlWeatherdictionary.
2. Theweatherelementmeansyouhavetheweatherforasubsequentday.Whilethereisonlyonecurrentday,
theremaybeseveralsubsequentdays,soyouaddthisweatherinformationtoanarray.
3. Thevaluetagonlyappearsinsideothertags,soitssafetoskipoverit.
4. TheweatherDescandweatherIconUrlelementvaluesneedtobeboxedinsideanarraybeforetheycanbe
stored.Thisway,theywillmatchhowtheJSONandplistversionsofthedataarestructuredexactly.
5. Allotherelementscanbestoredasis.
Nowforthefinaldelegatemethod!Pastethismethodjustafterthepreviousone:
- (void) parserDidEndDocument:(NSXMLParser *)parser
{
self.weather = @{@"data": self.xmlWeather};
self.title = @"XML Retrieved";
[self.tableView reloadData];
}
Theparsercallsthismethodwhenitreachestheendofthedocument.Atthispoint,thexmlWeatherdictionarythat
youvebeenbuildingiscomplete,sothetableviewcanbereloaded.
WrappingxmlWeatherinsideanotherNSDictionarymightseemredundant,butthisensurestheformatmatchesup
exactlywiththeJSONandplistversions.Thisway,allthreedataformatscanbedisplayedwiththesamecode!

http://www.raywenderlich.com/59255/afnetworking20tutorial

13/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

Nowthatthedelegatemethodsandpropertiesareinplace,returntothexmlTapped:methodanduncommentthelines
ofcodefrombefore:
- (IBAction)xmlTapped:(id)sender
{
...
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id
responseObject) {

NSXMLParser *XMLParser = (NSXMLParser *)responseObject;


[XMLParser setShouldProcessNamespaces:YES];

// These lines below were previously commented


XMLParser.delegate = self;
[XMLParser parse];
...
}
Buildandrunyourproject.TrytappingtheXMLbutton,andyoushouldseethis:

http://www.raywenderlich.com/59255/afnetworking20tutorial

14/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

A Little Weather Flair


Hmm,thatlooksdreary,likeaweeksworthofrainydays.Howcouldyoujazzuptheweatherinformationinyourtable
view?
TakeanotherpeakattheJSONformatfrombefore,andyouwillseethatthereareimageURLsforeachweatheritem.
Displayingtheseweatherimagesineachtableviewcellwouldaddsomevisualinteresttotheapp.
AFNetworkingaddsacategorytoUIImageViewthatletsyouloadimagesasynchronously,meaningtheUIwillremain
responsivewhileimagesaredownloadedinthebackground.Totakeadvantageofthis,firstaddthecategoryimportto
thetopofWTTableViewController.m:
#import "UIImageView+AFNetworking.h"
FindthetableView:cellForRowAtIndexPath:methodandpastethefollowingcodejustabovethefinalreturncellline
(thereshouldbeacommentmarkingthespot):
cell.textLabel.text = [daysWeather weatherDescription];

NSURL *url = [NSURL URLWithString:daysWeather.weatherIconURL];


NSURLRequest *request = [NSURLRequest requestWithURL:url];
UIImage *placeholderImage = [UIImage imageNamed:@"placeholder"];

http://www.raywenderlich.com/59255/afnetworking20tutorial

15/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

__weak UITableViewCell *weakCell = cell;

[cell.imageView setImageWithURLRequest:request
placeholderImage:placeholderImage
success:^(NSURLRequest *request, NSHTTPURLResponse
*response, UIImage *image) {

weakCell.imageView.image = image;
[weakCell setNeedsLayout];

} failure:nil];
UIImageView+AFNetworkingmakessetImageWithURLRequest:andseveralotherrelatedmethodsavailabletoyou.
Boththesuccessandfailureblocksareoptional,butifyoudoprovideasuccessblock,youmustexplicitlysettheimage
propertyontheimageview(orelseitwontbeset).Ifyoudontprovideasuccessblock,theimagewillautomaticallybe
setforyou.
Whenthecellisfirstcreated,itsimageviewwilldisplaytheplaceholderimageuntiltherealimagehasfinished
downloading.
Nowbuildandrunyourproject.Taponanyoftheoperationsyouveaddedsofar,andyoushouldseethis:

http://www.raywenderlich.com/59255/afnetworking20tutorial

16/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

Nice!Asynchronouslyloadingimageshasneverbeeneasier.

A RESTful Class
SofaryouvebeencreatingoneoffnetworkingoperationsusingAFHTTPRequestOperation.
Alternatively,AFHTTPRequestOperationManagerandAFHTTPSessionManageraredesignedtohelpyoueasily
interactwithasingle,webserviceendpoint.
BothoftheseallowyoutosetabaseURLandthenmakeseveralrequeststothesameendpoint.Bothcanalsomonitor
forchangesinconnectivity,encodeparameters,handlemultipartformrequests,enqueuebatchoperations,andhelp
youperformthefullsuiteofRESTfulverbs(GET,POST,PUT,andDELETE).
WhichoneshouldIuse?,youmightask.
IfyouretargetingiOS7andabove,useAFHTTPSessionManager,asinternallyitcreatesanduses
NSURLSessionandrelatedobjects.
IfyouretargetingiOS6andabove,useAFHTTPRequestOperationManager,whichhassimilarfunctionalityto
AFHTTPSessionManager,yetitusesNSURLConnectioninternallyinsteadofNSURLSession(whichisnt
availableiniOS6).Otherwise,theseclassesareverysimilarinfunctionality.
Inyourweatherappproject,youllbeusingAFHTTPSessionManagertoperformbothaGETandPUToperation.

Note:UnclearonwhatallthistalkisaboutREST,GET,andPOST?Checkoutthisexplanationofthesubject
WhatisREST?

UpdatetheclassdeclarationatthetopofWTTableViewController.htothefollowing:
@interface WTTableViewController : UITableViewController<NSXMLParserDelegate,
CLLocationManagerDelegate, UIActionSheetDelegate>
InWTTableViewController.m,findtheclientTapped:methodandreplaceitsimplementationwiththefollowing:
- (IBAction)clientTapped:(id)sender
{
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:@"AFHTTPSessionManager"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"HTTP GET", @"HTTP
POST", nil];
[actionSheet showFromBarButtonItem:sender animated:YES];
}
ThismethodcreatesanddisplaysanactionsheetaskingtheusertochoosebetweenaGETandPOSTrequest.Addthe
followingmethodattheendoftheclassimplementation(rightbefore@end)toimplementtheactionsheetdelegate
method:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:
(NSInteger)buttonIndex
{
if (buttonIndex == [actionSheet cancelButtonIndex]) {
// User pressed cancel -- abort
return;
http://www.raywenderlich.com/59255/afnetworking20tutorial

17/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

// 1
NSURL *baseURL = [NSURL URLWithString:BaseURLString];
NSDictionary *parameters = @{@"format": @"json"};

// 2
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]
initWithBaseURL:baseURL];
manager.responseSerializer = [AFJSONResponseSerializer serializer];

// 3
if (buttonIndex == 0) {
[manager GET:@"weather.php" parameters:parameters success:^(NSURLSessionDataTask
*task, id responseObject) {
self.weather = responseObject;
self.title = @"HTTP GET";
[self.tableView reloadData];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving
Weather"
message:[error
localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];
}];
}

// 4
else if (buttonIndex == 1) {
[manager POST:@"weather.php" parameters:parameters success:^(NSURLSessionDataTask
*task, id responseObject) {
self.weather = responseObject;
self.title = @"HTTP POST";
[self.tableView reloadData];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving
Weather"
message:[error
localizedDescription]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];
}];
}
}
Hereswhatshappeningabove:
1. YoufirstsetupthebaseURLandthedictionaryofparameters.
2. YouthencreateaninstanceofAFHTTPSessionManagerandsetitsresponseSerializertothedefaultJSON
serializer,similartothepreviousJSONexample.
3. IftheuserpressesthebuttonindexforHTTPGET,youcalltheGETmethodonthemanager,passinginthe
parametersandusualpairofsuccessandfailureblocks.
4. YoudothesamewiththePOSTversion.
http://www.raywenderlich.com/59255/afnetworking20tutorial

18/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

InthisexampleyourerequestingJSONresponses,butyoucaneasilyrequesteitheroftheothertwoformatsas
discussedpreviously.
Buildandrunyourproject,tapontheClientbuttonandthentaponeithertheHTTPGETorHTTPPOSTbuttontoinitiate
theassociatedrequest.Youshouldseethesescreens:

Atthispoint,youknowthebasicsofusingAFHTTPSessionManager,buttheresanevenbetterwaytouseitthatwill
resultincleanercode,whichyoulllearnaboutnext.

World Weather Online


Beforeyoucanusetheliveservice,youllfirstneedtoregisterforafreeaccountonWorldWeatherOnline.Dontworry
itsquickandeasytodo!
Afteryouveregistered,youshouldreceiveaconfirmationemailattheaddressyouprovided,whichwillhavealinkto
confirmyouremailaddress(required).YouthenneedtorequestafreeAPIkeyviatheMyAccountpage.Goaheadand
leavethepageopenwithyourAPIkeyasyoullneeditsoon.
NowthatyouvegotyourAPIkey,backtoAFNetworking

Hooking into the Live Service


SofaryouvebeencreatingAFHTTPRequestOperationandAFHTTPSessionManagerdirectlyfromthetableview
controllerasyouneededthem.Moreoftenthannot,yournetworkingrequestswillbeassociatedwithasingleweb
serviceorAPI.
AFHTTPSessionManagerhaseverythingyouneedtotalktoawebAPI.Itwilldecoupleyournetworkingcommunications
codefromtherestofyourcode,andmakeyournetworkingcommunicationscodereusablethroughoutyourproject.
HerearetwoguidelinesonAFHTTPSessionManagerbestpractices:
http://www.raywenderlich.com/59255/afnetworking20tutorial

19/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

1. Createasubclassforeachwebservice.Forexample,ifyourewritingasocialnetworkaggregator,youmightwant
onesubclassforTwitter,oneforFacebook,anotherforInstragramandsoon.
2. IneachAFHTTPSessionManagersubclass,createaclassmethodthatreturnsasharedsingletoninstance.This
savesresourcesandeliminatestheneedtoallocateandspinupnewobjects.
YourprojectcurrentlydoesnthaveasubclassofAFHTTPSessionManageritjustcreatesonedirectly.Letsfixthat.
Tobegin,createanewfileinyourprojectoftypeiOS\CocoaTouch\ObjectiveCClass.CallitWeatherHTTPClientand
makeitasubclassofAFHTTPSessionManager.
Youwanttheclasstodothreethings:performHTTPrequests,callbacktoadelegatewhenthenewweatherdatais
available,andusetheusersphysicallocationtogetaccurateweather.
ReplacethecontentsofWeatherHTTPClient.hwiththefollowing:
#import "AFHTTPSessionManager.h"

@protocol WeatherHTTPClientDelegate;

@interface WeatherHTTPClient : AFHTTPSessionManager


@property (nonatomic, weak) id<WeatherHTTPClientDelegate>delegate;

+ (WeatherHTTPClient *)sharedWeatherHTTPClient;
- (instancetype)initWithBaseURL:(NSURL *)url;
- (void)updateWeatherAtLocation:(CLLocation *)location forNumberOfDays:(NSUInteger)number;

@end

@protocol WeatherHTTPClientDelegate <NSObject>


@optional
-(void)weatherHTTPClient:(WeatherHTTPClient *)client didUpdateWithWeather:(id)weather;
-(void)weatherHTTPClient:(WeatherHTTPClient *)client didFailWithError:(NSError *)error;
@end
Youlllearnmoreabouteachofthesemethodsasyouimplementthem.SwitchovertoWeatherHTTPClient.mandadd
thefollowingrightaftertheimportstatement:
// Set this to your World Weather Online API Key
static NSString * const WorldWeatherOnlineAPIKey = @"PASTE YOUR API KEY HERE";

static NSString * const WorldWeatherOnlineURLString =


@"http://api.worldweatheronline.com/free/v1/";
Makesureyoureplace@PASTEYOURKEYHEREwithyouractualWorldWeatherOnlineAPIKey.
Nextpastethesemethodsjustafterthe@implementationline:
+ (WeatherHTTPClient *)sharedWeatherHTTPClient
{
static WeatherHTTPClient *_sharedWeatherHTTPClient = nil;

static dispatch_once_t onceToken;


dispatch_once(&onceToken, ^{
_sharedWeatherHTTPClient = [[self alloc] initWithBaseURL:[NSURL
URLWithString:WorldWeatherOnlineURLString]];
});

return _sharedWeatherHTTPClient;
}

- (instancetype)initWithBaseURL:(NSURL *)url
http://www.raywenderlich.com/59255/afnetworking20tutorial

20/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

{
self = [super initWithBaseURL:url];

if (self) {
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.requestSerializer = [AFJSONRequestSerializer serializer];
}

return self;
}
ThesharedWeatherHTTPClientmethodusesGrandCentralDispatchtoensurethesharedsingletonobjectisonly
allocatedonce.YouinitializetheobjectwithabaseURLandsetituptorequestandexpectJSONresponsesfromthe
webservice.
Pastethefollowingmethodunderneaththepreviousones:
- (void)updateWeatherAtLocation:(CLLocation *)location forNumberOfDays:(NSUInteger)number
{
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];

parameters[@"num_of_days"] = @(number);
parameters[@"q"] = [NSString
stringWithFormat:@"%f,%f",location.coordinate.latitude,location.coordinate.longitude];
parameters[@"format"] = @"json";
parameters[@"key"] = WorldWeatherOnlineAPIKey;

[self GET:@"weather.ashx" parameters:parameters success:^(NSURLSessionDataTask *task,


id responseObject) {
if ([self.delegate
respondsToSelector:@selector(weatherHTTPClient:didUpdateWithWeather:)]) {
[self.delegate weatherHTTPClient:self didUpdateWithWeather:responseObject];
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if ([self.delegate
respondsToSelector:@selector(weatherHTTPClient:didFailWithError:)]) {
[self.delegate weatherHTTPClient:self didFailWithError:error];
}
}];
}
ThismethodcallsouttoWorldWeatherOnlinetogettheweatherforaparticularlocation.
Oncetheobjecthasloadedtheweatherdata,itneedssomewaytocommunicatethatdatabacktowhoeversinterested.
ThankstotheWeatherHTTPClientDelegateprotocolanditsdelegatemethods,thesuccessandfailureblocksinthe
abovecodecannotifyacontrollerthattheweatherhasbeenupdatedforagivenlocation.Thatway,thecontrollercan
updatewhatitisdisplaying.
Nowitstimetoputthefinalpiecestogether!TheWeatherHTTPClientisexpectingalocationandhasadefineddelegate
protocol,soyouneedtoupdatetheWTTableViewControllerclasstotakeadvantageofthis.
OpenupWTTableViewController.htoaddanimportandreplacethe@interfacedeclarationasfollows:
#import "WeatherHTTPClient.h"

@interface WTTableViewController : UITableViewController <NSXMLParserDelegate,


CLLocationManagerDelegate, UIActionSheetDelegate, WeatherHTTPClientDelegate>
AlsoaddanewCoreLocationmanagerproperty:
@property (nonatomic, strong) CLLocationManager *locationManager;
http://www.raywenderlich.com/59255/afnetworking20tutorial

21/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

InWTTableViewController.m,addthefollowinglinestothebottomofviewDidLoad::
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
TheselinesinitializetheCoreLocationmanagertodeterminetheuserslocationwhentheviewloads.TheCore
Locationmanagerthenreportsthatlocationviaadelegatecallback.Addthefollowingmethodtotheimplementation:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray
*)locations
{
// Last object contains the most recent location
CLLocation *newLocation = [locations lastObject];

// If the location is more than 5 minutes old, ignore it


if([newLocation.timestamp timeIntervalSinceNow] > 300)
return;

[self.locationManager stopUpdatingLocation];

WeatherHTTPClient *client = [WeatherHTTPClient sharedWeatherHTTPClient];


client.delegate = self;
[client updateWeatherAtLocation:newLocation forNumberOfDays:5];
}
Nowwhentheresanupdatetotheuserswhereabouts,youcancallthesingletonWeatherHTTPClientinstanceto
requesttheweatherforthecurrentlocation.
Remember,WeatherHTTPClienthastwodelegatemethodsitselfthatyouneedtoimplement.Addthefollowingtwo
methodstotheimplementation:
- (void)weatherHTTPClient:(WeatherHTTPClient *)client didUpdateWithWeather:(id)weather
{
self.weather = weather;
self.title = @"API Updated";
[self.tableView reloadData];
}

- (void)weatherHTTPClient:(WeatherHTTPClient *)client didFailWithError:(NSError *)error


{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving
Weather"
message:[NSString
stringWithFormat:@"%@",error]
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
}
WhentheWeatherHTTPClientsucceeds,youupdatetheweatherdataandreloadthetableview.Incaseofanetwork
error,youdisplayanerrormessage.
FindtheapiTapped:methodandreplaceitwiththefollowing:
- (IBAction)apiTapped:(id)sender
{
[self.locationManager startUpdatingLocation];
}
Buildandrunyourproject(tryyourdeviceifyouhaveanytroubleswithyoursimulator),tapontheAPIbuttontoinitiate
http://www.raywenderlich.com/59255/afnetworking20tutorial

22/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

theWeatherHTTPClientrequest,andyoushouldseesomethinglikethis:

Hereshopingyourupcomingweatherisassunnyasmine!

Im Not Dead Yet!


Youmighthavenoticedthatthisexternalwebservicecantakesometimebeforeitreturnswithdata.Itsimportantto
provideyouruserswithfeedbackwhendoingnetworkoperationssotheyknowtheapphasntstalledorcrashed.

http://www.raywenderlich.com/59255/afnetworking20tutorial

23/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

Luckily,AFNetworkingcomeswithaneasywaytoprovidethisfeedback:AFNetworkActivityIndicatorManager.
InWTAppDelegate.m,addthisimportjustbelowtheother:
#import "AFNetworkActivityIndicatorManager.h"
Thenfindtheapplication:didFinishLaunchingWithOptions:methodandreplaceitwiththefollowing:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions
{
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
return YES;
}
EnablingthesharedManagerautomaticallydisplaysthenetworkactivityindicatorwheneveranewoperationis
underway.Youwontneedtomanageitseparatelyforeveryrequestyoumake.
Buildandrun,andyoushouldseethelittlenetworkingspinnerinthestatusbarwhenevertheresanetworkrequest:

Nowtheresasignoflifeforyouruserevenwhenyourappiswaitingonaslowwebservice.

Downloading Images
Ifyoutaponatableviewcell,theapptakesyoutoadetailviewoftheweatherandananimationillustratingthe
correspondingweatherconditions.
Thatsnice,butatthemomenttheanimationhasaveryplainbackground.Whatbetterwaytoupdatethebackground
thanoverthenetwork!
HeresthefinalAFNetworkingtrickforthistutorial:AFHTTPRequestOperationcanalsohandleimagerequestsbysetting
itsresponseSerializertoaninstanceofAFImageResponseSerializer.
TherearetwomethodstubsinWeatherAnimationViewController.mtoimplement.FindtheupdateBackgroundImage:
methodandreplaceitwiththefollowing:
- (IBAction)updateBackgroundImage:(id)sender
http://www.raywenderlich.com/59255/afnetworking20tutorial

24/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

{
NSURL *url = [NSURL URLWithString:@"http://www.raywenderlich.com/wpcontent/uploads/2014/01/sunny-background.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]


initWithRequest:request];
operation.responseSerializer = [AFImageResponseSerializer serializer];

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id


responseObject) {

self.backgroundImageView.image = responseObject;
[self saveImage:responseObject withFilename:@"background.png"];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

NSLog(@"Error: %@", error);


}];

[operation start];
}
Thismethodinitiatesandhandlesdownloadingthenewbackground.Oncompletion,itreturnsthefullimagerequested.
InWeatherAnimationViewController.m,youwillseetwohelpermethods,imageWithFilename:and
saveImage:withFilename:,whichwillletyoustoreandloadanyimageyoudownload.updateBackgroundImage:calls
thesehelpermethodstosavethedownloadedimagestodisk.
FindthedeleteBackgroundImage:methodandreplaceitwiththefollowing:
- (IBAction)deleteBackgroundImage:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0]
stringByAppendingPathComponent:@"WeatherHTTPClientImages/"];

NSError *error = nil;


[[NSFileManager defaultManager] removeItemAtPath:path error:&error];

NSString *desc = [self.weatherDictionary weatherDescription];


[self start:desc];
}
Thismethoddeletesthedownloadedbackgroundimagesothatyoucandownloaditagainwhentestingtheapplication.
Forthefinaltime:buildandrun,downloadtheweatherdataandtaponacelltogettothedetailedview.Fromhere,tap
theUpdateBackgroundbutton.IfyoutaponaSunnycell,youshouldseethis:

http://www.raywenderlich.com/59255/afnetworking20tutorial

25/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

Where To Go From Here?


Youcandownloadthecompletedprojectfromhere.
ThinkofallthewaysyoucannowuseAFNetworkingtocommunicatewiththeoutsideworld:
AFHTTPOperationwithAFJSONResponseSerializer,AFPropertyListResponseSerializer,or
AFXMLParserResponseSerializerresponseserializersforparsingstructureddata
UIImageView+AFNetworkingforquicklyfillinginimageviews
CustomAFHTTPSessionManagersubclassestoaccesslivewebservices
AFNetworkActivityIndicatorManagertokeeptheuserinformed
AFHTTPOperationwithaAFImageResponseSerializerresponseserializerforloadingimages
ThepowerofAFNetworkingisyourstodeploy!
Ifyouhaveanyquestionsaboutanythingyouveseenhere,pleasepayavisittotheforumstogetsomeassistance.Id
alsolovetoreadyourcomments!

http://www.raywenderlich.com/59255/afnetworking20tutorial

26/27

6/16/2015

AFNetworking2.0TutorialRayWenderlich

JoshuaGreene
JoshuaGreeneisasenioriOSdeveloperatCitrix.Whenhe'snotslingingcode,he
enjoysmartialarts,Netflix,andspendingtimewithhiswonderfulwifeanddaughter.
YoucanreachhimbyemailoronTwitter.

http://www.raywenderlich.com/59255/afnetworking20tutorial

27/27

Das könnte Ihnen auch gefallen