AFNetworking 速成教程3
2014-11-13 10:56
363 查看
一个RESTful类
到现在你已经使用类似AFJSONRequestOperation这样的类创建了一次性的HTTP请求。另外,较低级的AFHTTPClient类是用来访问单个的web service终端。 对这个AFHTTPClient一般是给它设置一个基本的URL,然后用AFHTTPClient进行多个请求(而不是像之前的那样,每次请求的时候,都创建一个AFHTTPClient)。AFHTTPClient同样为编码参数、处理multipart表单请求body的构造、管理请求操作和批次入队列操作提供了很强的灵活性,它还处理了整套RESTful (GET, POST, PUT, 和 DELETE), 下面我们就来试试最常用的两个:GET 和 POST.
注意: 对REST, GET和POST不清楚?看看这里比较有趣的介绍 – 我如何给妻子解释REST(How
I Explained REST to My Wife.)
在WTTableViewController.h 顶部将类声明按照如下修改:
@interface WTTableViewController : UITableViewController |
- (IBAction)httpClientTapped:(id)sender { UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"AFHTTPClient" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"HTTP POST",@"HTTP GET", nil]; [actionSheet showFromBarButtonItem:sender animated:YES]; } |
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ // 1 NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:BaseURLString]]; NSDictionary *parameters = [NSDictionary dictionaryWithObject:@"json" forKey:@"format"]; // 2 AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL]; [client registerHTTPOperationClass:[AFJSONRequestOperation class]]; [client setDefaultHeader:@"Accept" value:@"application/json"]; // 3 if (buttonIndex==0) { [client postPath:@"weather.php" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { self.weather = responseObject; self.title = @"HTTP POST"; [self.tableView reloadData]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Weather" message:[NSString stringWithFormat:@"%@",error] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [av show]; } ]; } // 4 else if (buttonIndex==1) { [client getPath:@"weather.php" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { self.weather = responseObject; self.title = @"HTTP GET"; [self.tableView reloadData]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Weather" message:[NSString stringWithFormat:@"%@",error] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [av show]; } ]; } } |
构建一个baseURL,以及一个参数字典,并将这两个变量传给AFHTTPClient.
将AFJSONRequestOperation注册为HTTP的操作, 这样就可以跟之前的示例一样,可以获得解析好的JSON数据。
做了一个GET请求,这个请求有一对block:success和failure。
POST请求跟GET一样。
在这里,将请求一个JSON回应,当然也可以使用之前讨论过的另外两种格式来代替JSON。
生成并运行工程,点击HTTPClient按钮,然后选择GET 或 POST按钮来初始化一个相关的请求。之后会看到如下内容:
至此,你已经知道AFHTTPClient最基本的使用方法。不过,这里还有更好的一种使用方法,它可以让代码更加干净整齐,下面我们就来学习一下吧。
连接到Live Service
到现在为止,你已经在table view controller中直接调用了AFRequestOperations 和 AFHTTPClient. 实际上,大多数时候不是这样的,你的网络请求会跟某个web service或API相关。AFHTTPClient已经具备与web API通讯的所有内容。AFHTTPClient在代码中已经把网络通讯部分做了解耦处理,让网络通讯的代码在整个工程中都可以重用。
下面是两个关于AFHTTPClient最佳实践的指导:
为每个web service创建一个子类。例如,如果你在写一个社交网络聚合器,那么可能就会有Twitter的一个子类,Facebook的一个子类,Instragram的一个子类等等。
在AFHTTPClient子类中,创建一个类方法,用来返回一个共享的单例,这将会节约资源并省去必要的对象创建。
当前,你的工程中还没有一个AFHTTPClient的子类,下面就来创建一个吧。我们来处理一下,让代码清洁起来。
首先,在工程中创建一个新的文件:iOSCocoa TouchObjective-C Class. 命名为WeatherHTTPClient 并让其继承自AFHTTPClient.
你希望这个类做3件事情:
A:执行HTTP请求
B:当有新的可用天气数据时,调用delegate
C:使用用户当前地理位置来获得准确的天气。
用下面的代码替换WeatherHTTPClient.h:
#import "AFHTTPClient.h" @protocol WeatherHttpClientDelegate; @interface WeatherHTTPClient : AFHTTPClient @property(weak) id delegate; + (WeatherHTTPClient *)sharedWeatherHTTPClient; - (id)initWithBaseURL:(NSURL *)url; - (void)updateWeatherAtLocation:(CLLocation *)location forNumberOfDays:(int)number; @end @protocol WeatherHttpClientDelegate -(void)weatherHTTPClient:(WeatherHTTPClient *)client didUpdateWithWeather:(id)weather; -(void)weatherHTTPClient:(WeatherHTTPClient *)client didFailWithError:(NSError *)error; @end |
+ (WeatherHTTPClient *)sharedWeatherHTTPClient { NSString *urlStr = @"http://free.worldweatheronline.com/feed/"; static dispatch_once_t pred; static WeatherHTTPClient *_sharedWeatherHTTPClient = nil; dispatch_once(&pred, ^{ _sharedWeatherHTTPClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:urlStr]]; }); return _sharedWeatherHTTPClient; } - (id)initWithBaseURL:(NSURL *)url { self = [super initWithBaseURL:url]; if (!self) { return nil; } [self registerHTTPOperationClass:[AFJSONRequestOperation class]]; [self setDefaultHeader:@"Accept" value:@"application/json"]; return self; } |
将下面的方法粘贴到上一个方法的下面:
- (void)updateWeatherAtLocation:(CLLocation *)location forNumberOfDays:(int)number{ NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; [parameters setObject:[NSString stringWithFormat:@"%d",number] forKey:@"num_of_days"]; [parameters setObject:[NSString stringWithFormat:@"%f,%f",location.coordinate.latitude,location.coordinate.longitude] forKey:@"q"]; [parameters setObject:@"json" forKey:@"format"]; [parameters setObject:@"7f3a3480fc162445131401" forKey:@"key"]; [self getPath:@"weather.ashx" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { if([self.delegate respondsToSelector:@selector(weatherHTTPClient:didUpdateWithWeather:)]) [self.delegate weatherHTTPClient:self didUpdateWithWeather:responseObject]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if([self.delegate respondsToSelector:@selector(weatherHTTPClient:didFailWithError:)]) [self.delegate weatherHTTPClient:self didFailWithError:error]; }]; } |
非常重要!本实例中的API key仅仅是为本文创建的。如果你创建了一个程序,请在World
Weather Online创建一个账号,并获得你自己的API key!
一旦对象获得了天气数据,它需要一些方法来通知对此感兴趣的对象:数据回来了。这里要感谢WeatherHttpClientDelegate 协议和它的delegate方法,在上面代码中的success 和 failure blocks可以通知一个controller:指定位置的天气已经更新了。这样,controller就可以对天气做更新显示。
现在,我们需要把这些代码片段整合到一起!WeatherHTTPClient希望接收一个位置信息,并且WeatherHTTPClient定义了一个delegate协议,现在对WTTableViewControlle类做一下更新,以使用WeatherHTTPClient.
打开WTTableViewController.h 添加一个import,并用下面的代码替换@interface声明:
#import "WeatherHTTPClient.h" |
@property(strong) CLLocationManager *manager; |
self.manager = [[CLLocationManager alloc] init]; self.manager.delegate = self; |
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{ //if the location is more than 5 minutes old ignore if([newLocation.timestamp timeIntervalSinceNow]< 300){ [self.manager stopUpdatingLocation]; WeatherHTTPClient *client = [WeatherHTTPClient sharedWeatherHTTPClient]; client.delegate = self; [client updateWeatherAtLocation:newLocation forNumberOfDays:5]; } } |
记住,WeatherHTTPClient有两个delegate方法需要实现。将下面两个方法添加到实现文件中:
-(void)weatherHTTPClient:(WeatherHTTPClient *)client didUpdateWithWeather:(id)aWeather{ self.weather = aWeather; self.title = @"API Updated"; [self.tableView reloadData]; } -(void)weatherHTTPClient:(WeatherHTTPClient *)client didFailWithError:(NSError *)error{ UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Weather" message:[NSString stringWithFormat:@"%@",error] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [av show]; } |
找到apiTapped: 方法,并用下面的方法替换:
-(IBAction)apiTapped:(id)sender{ [self.manager startUpdatingLocation]; } |
希望在这里你未来的天气跟我的一样:晴天!
我还没有死!
你可能注意到了,这里调用的外部web service需要花费一些时间才能返回数据。当在进行网络操作时,给用户提供一个信息反馈是非常重要的,这样用户才知道程序是在运行中或已奔溃了。很幸运的是,AFNetworking有一个简便的方法来提供信息反馈:AFNetworkActivityIndicatorManager.
在 WTAppDelegate.m中,找到application:didFinishLaunchingWithOptions: 方法,并用下面的方法替换:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [AFNetworkActivityIndicatorManager sharedManager].enabled = YES; return YES; } |
生成并运行工程,无论什么时候,只要有网络请求,都可以在状态栏中看到一个小的网络风火轮:
现在,即使你的程序在等待一个很慢的web service,用户都知道程序还在运行着!
下载图片
如果你在table view cell上点击,程序会切换到天气的详细画面,并且以动画的方式显示出相应的天气情况。这非常不错,但目前动画只有一个背景图片。除了通过网络来更新背景图片,还有更好的方法吗!
下面是本文关于介绍AFNetworking的最后内容了:AFImageRequestOperation. 跟AFJSONRequestOperation一样, AFImageRequestOperation封装了HTTP请求:获取图片。
在WeatherAnimationViewController.m 中有两个方法需要实现. 找到updateBackgroundImage: 方法,并用下面的代码替换:
- (IBAction)updateBackgroundImage:(id)sender { //Store this image on the same server as the weather canned files NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.scott-sherwood.com/wp-content/uploads/2013/01/scene.png"]]; AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { self.backgroundImageView.image = image; [self saveImage:image withFilename:@"background.png"]; } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"Error %@",error); }]; [operation start]; } |
在WeatherAnimationViewController.m中, 你将看到两个辅助方法:imageWithFilename: 和 saveImage:withFilename:, 通过这两个辅助方法,可以对下载下来的图片进行存储和加载。updateBackgroundImage: 将通过辅助方法把下载的图片存储到磁盘中。
接下来找到deleteBackgroundImage: 方法,并用下面的代码替换:
- (IBAction)deleteBackgroundImage:(id)sender { NSString *path; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"WeatherHTTPClientImages/"]; NSError *error; [[NSFileManager defaultManager] removeItemAtPath:path error:&error]; NSString *desc = [self.weatherDictionary weatherDescription]; [self start:desc]; } |
最后一次:生成并运行工程,下载天气数据,并点击某个cell,以打开详细天气画面。在详细天气画面中,点击Update Background 按钮. 如果你点击的是晴天cell,将会看到如下画面:
相关文章推荐
- iOS之AFNetworking 速成教程1
- iOS之AFNetworking 速成教程2
- AFNetworking速成教程
- iOS之AFNetworking 速成教程3
- AFNetworking速成教程(1)
- AFNetworking速成教程(1)
- AFNetworking速成教程(1)
- AFNetworking速成教程
- AFNetworking速成教程
- AFNetworking速成教程(2)
- AFNetworking速成教程
- AFNetworking速成教程(1)
- iOS-raywenderlich翻译-AFNetworking速成教程
- AFNetworking速成教程
- AFNetworking速成教程
- AFNetworking速成教程
- AFNetworking速成教程(1)
- AFNetworking速成教程
- AFNetworking速成教程(2)
- AFNetworking速成教程(1)