转载

NSURLSession使用实战教程

我的前面两篇文章介绍了NSURLSession套件的使用和NSURLSession套件的主要类。今天我们使用NSURLSession来完成一个小的应用程序。在实战之前,我先补充一点,为什么苹果会主推NSURLSession技术,而放弃大家都熟悉的NSURLConnection技术,苹果这么做肯定是有原因的,下面列举了NSURLSession的优点:

1.后台上传和下载。当你的程序退出了也能进行网络操作,这对用户和APP来说都是个好消息,不用运行APP就可以下载和上传,这样更节约手机电量。

2.能够暂停和恢复网络操作。不需要使用NSOperation就可以实现暂停、继续、重启等操作。

3.可配置的容器。

4.可以子类化并且可以设置私有存储方式。可以修改数据的存储方式和存储位置。

5.改进了授权处理机制。

6.代理更强大。

7.通过文件系统上传和下载。

好了,进入整体开始我们的实战,开发一个小的APP叫《ByteClub》。这篇文章的实战我是参考国外的网站做的,原文《NSURLSession Tutorial》,地址:http://www.raywenderlich.com/51127/nsurlsession-tutorial。觉得它有点啰嗦,英文好的也可以看原文。

我没有去原文翻译它,参考它做完例子之后,我按照自己的思路写的本教程。

准备工作

1.如果你打算跟我一起动手做的话,您需要一个FQ工具,因为我需要使用dropbox(类似百度云盘)做http网络服务器,它在国内被墙掉了:cry:,我使用的是lantern.下载地址:http://pan.baidu.com/s/1hqhQqHI。下载完记得一定要安装和运行起来。

2.在dropbox网站注册成为开发者,然后创建一个APP。Dropbox开发者应用注册地址:https://www.dropbox.com/developers/apps。

3.然后下载dropbox for mac,我自己准备好了安装文件DropboxInstaller.dmg,下载地址:http://pan.baidu.com/s/1sjDvZNB。然后安装,安装好后在跟目录下创建byteclub的目录。如图:

NSURLSession使用实战教程

4.请在byteclub目录中,随便创建或者复制进来几个文件,然后会同步到服务器上,然后我们的第一步开发工作,就是从服务器读取到这些文件的文件名。

5.然后再下载起始项目,代码把UI界面以及dropbox的http接口做好了封装,以便我们专注于NSURLSession部分的实战和学习。起始项目代码下载地址:http://pan.baidu.com/s/1kTMvCZ9。

6.如果您想查看最终效果,可以下载我实战完成后的代码。下载地址:http://pan.baidu.com/s/1qWJ3GDY

项目完成后的效果图:

NSURLSession使用实战教程

您可以先稍微熟悉下初始项目。

开工干活

第一阶段:读取Dropbox的跟目录文件名,并显示。

1.打开Dropbox.m将您的apiKey和appSecret,appFolder设置进去,前两者认证需呀,后者是我们在准备阶段在Dropbox创建的目录,比如我的设置为:

static NSString *apiKey = @"rctz909lpd47vyq"; static NSString *appSecret = @“odz1qfezg4ij3pz"; NSString * const appFolder = @“byteclub";

然后你可以运行看看,按照引导信息,输入您在dropbox的账号信息,然后应该就可以认证通过了。

2.在NotesViewController.m文件中添加一个会话属性,用于保存我们的会话。

/**  *   会话  */ @property (nonatomic, strong) NSURLSession *session;

3.在initWithStyle:前面添加另外一个初始化方法:

- (instancetype)initWithCoder:(NSCoder *)aDecoder {  self = [super initWithCoder:aDecoder];  //下面内容为创建会话  if (self) {   //会话配置,这里配置为短暂配置,还有默认配置和后台配置   NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];   //配置请求头   [config setHTTPAdditionalHeaders:@{@"Authorization":[Dropbox apiAuthorizationHeader]}];   //初始化会话   _session = [NSURLSession sessionWithConfiguration:config];  }  return self; } 

4.找到notesOnDropbox:方法,然方法内输入如下代码:

//显示加载提示 [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; //获取你的Dropbox的根目录 NSURL *url = [Dropbox appRootURL]; //创建数据任务,这个方法主要用来请求HTTP的GET方法,并返回NSData对象,我们需要将数据再解析成我们需要的数据 NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {  if (!error) {   // 响应状态代码为200,代表请求数据成功,判断成功后我们再进行数据解析   NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;   if (httpResp.statusCode == 200) {    NSError *jsonError;        //解析NSData数据    NSDictionary *notesJSON =    [NSJSONSerialization JSONObjectWithData:data            options:NSJSONReadingAllowFragments              error:&jsonError];    NSMutableArray *notesFound = [[NSMutableArray alloc] init];    if (!jsonError) {          // 获取contents键值,文件路径保存在这里     NSArray *contentsOfRootDirectory = notesJSON[@"contents"];     for (NSDictionary *data in contentsOfRootDirectory) {      if (![data[@"is_dir"] boolValue]) {       DBFile *note = [[DBFile alloc] initWithJSONData:data];       [notesFound addObject:note];      }     }      //排序     [notesFound sortUsingComparator:      ^NSComparisonResult(id obj1, id obj2) {       return [obj1 compare:obj2];      }];     self.notes = notesFound;     // NSURLSession的方法是在异步执行的,所以更新UI回到主线程     dispatch_async(dispatch_get_main_queue(), ^{      [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;      [self.tableView reloadData];     });    }   }  } }]; //启动任务 [dataTask resume]; 

注释已经比较清晰,如果我们查看Dropbox的文档就会发现,私用这个URL返回的数据就是json数据,数据格式如下:

{  "hash": "6a29b68d106bda4473ffdaf2e94c4b61",  "revision": 73052,  "rev": "11d5c00e1cf6c",  "thumb_exists": false,  "bytes": 0,  "modified": "Sat, 10 Aug 2013 21:56:50 +0000",  "path": "/byteclub",  "is_dir": true,  "icon": "folder",  "root": "dropbox",  "contents": [{   "revision": 73054,   "rev": "11d5e00e1cf6c",   "thumb_exists": false,   "bytes": 16,   "modified": "Sat, 10 Aug 2013 23:21:03 +0000",   "client_mtime": "Sat, 10 Aug 2013 23:21:02 +0000",   "path": "/byteclub/test.txt",   "is_dir": false,   "icon": "page_white_text",   "root": "dropbox",   "mime_type": "text/plain",   "size": "16 bytes"  }],  "size": "0 bytes" } 

dropbox服务器的返回状态码有如下一些:

400 –代表参数有误.

401 – token错误或过期.

403 – 错误 OAuth 请求

404 – 请求的文件和目录不存在

405 – 请求方法错误,一般我们只使用get 和post

429 –程序请求次数过多

503 –请再次尝试

507 –使用Dropbox空间超过配额限制

5xx – 服务器错误

5.运行一下看看效果,我的效果是这样的:

NSURLSession使用实战教程

第一阶段的实战完成,你可以先复习一下内容,然后再进行第二阶段的实战。

第二阶段的实战

通过APP输入内容,并通过文件的形式保存到Dropbox服务器并然后显示。

1.点击右上角的“+”按钮,跳转到添加Notes界面,你可以输入内容,但目前没什么作用,我们需要实现这些功能。打开NotesViewController.m文件,找到prepareForSegue:sender:跳转方法,在代码showNote.delegate = self后添加下面一行代码,将会话session传递到新的页面,这样我们就可以在新的页面使用同一个会话了:

//传递会话     showNote.session = _session;

2.找到新页面所属类的实现文件NoteDetailsViewController.m文件,找到 (IBAction)done:(id)sender方法,这是我们点击done按钮需要执行的方法,替换为如下代码:

- (IBAction)done:(id)sender {  // must contain text in textview  if (![_textView.text isEqualToString:@""]) {   // check to see if we are adding a new note   if (!self.note) {    DBFile *newNote = [[DBFile alloc] init];    newNote.root = @"dropbox";    self.note = newNote;   }   _note.contents = _textView.text;   _note.path = _filename.text;   // - 上传文件到 DROPBOX - //   // 获取需要上传文件的路径   NSURL *url = [Dropbox uploadURLForPath:_note.path];   // 创建请求,这里使用了put方法   NSMutableURLRequest *request =   [[NSMutableURLRequest alloc] initWithURL:url];   [request setHTTPMethod:@"PUT"];   //数据   NSData *noteContents = [_note.contents dataUsingEncoding:NSUTF8StringEncoding];   // 上传任务,NSURLSessionUploadTask支持文件,NSData,数据流stream的类型数据上传   NSURLSessionUploadTask *uploadTask = [_session              uploadTaskWithRequest:request              fromData:noteContents              completionHandler:^(NSData *data,                   NSURLResponse *response,                   NSError *error)   {    //根据HTTP返回的代号确定是否成功,200代表成功,成功后我调用代理方法    NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;    if (!error && httpResp.statusCode == 200) {     [self.delegate noteDetailsViewControllerDoneWithDetails:self];    } else {     // alert for error saving / updating note    }   }];   // 必须要的动作,启动任务   [uploadTask resume];  } else {   UIAlertView *noTextAlert = [[UIAlertView alloc] initWithTitle:@"输入为空"                  message:@"总得输入点啥吧,亲"                 delegate:nil              cancelButtonTitle:@"好"              otherButtonTitles:nil];   [noTextAlert show];  } } 

3.打开NoteDetailsViewController.m方法,找到方法retreiveNoteText:,替换为如下内容:

-(void)retreiveNoteText {  // 根据Dropbox API设置要查看的note的请求路径  NSString *fileApi =  @"https://api-content.dropbox.com/1/files/dropbox";  NSString *escapedPath = [_note.path         stringByAddingPercentEscapesUsingEncoding:         NSUTF8StringEncoding];  NSString *urlStr = [NSString stringWithFormat: @"%@/%@",       fileApi,escapedPath];  NSURL *url = [NSURL URLWithString: urlStr];  [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;  // 执行下载数据任务  [[_session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {   if (!error) {    NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;    if (httpResp.statusCode == 200) {     // 数据转字符串     NSString *text =     [[NSString alloc]initWithData:data           encoding:NSUTF8StringEncoding];     dispatch_async(dispatch_get_main_queue(), ^{      [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;      self.textView.text = text;     });    } else {     // 处理错误的响应 //    }   } else {    // 处理意外错误 //   }   // 一定要创建任务后启动哦  }] resume]; } 

4.运行看看,比如输入内容,点击done按钮,过一会儿你查看下你的byteclub目录,应该会创建一个新的文件。我的运行效果是这样的:

NSURLSession使用实战教程

好了,我们的第二阶段任务就完成,还是建议您复习一下刚才的内容,然后我们开始第三阶段的实战。

第三阶段

使用NSURLSessionTask的代理方法发送图片到dropbox

1.请在byteclub目录下新建一个photos目录,然后拖一些你的图片到里面来,等待一会儿,图片应该就会上传完。

NSURLSession使用实战教程

2.打开PhotosViewController.m,找到tableView:cellForRowAtIndexPath:方法,替换为下面内容:

tableView:cellForRowAtIndexPath:

3.相同文件,找到refreshPhotos方法,替换为下面内容:

//获取图片 - (void)refreshPhotos {  //网络家提示开启  [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];  //dropbox的API,这个接口用于搜索图片  NSString *photoDir = [NSString stringWithFormat:@"https://api.dropbox.com/1/search/dropbox/%@/photos?query=.jpg",appFolder];  NSURL *url = [NSURL URLWithString:photoDir];  //启动一个数据下载任务  [[_session dataTaskWithURL:url completionHandler:^(NSData                 *data, NSURLResponse *response, NSError *error) {   if (!error) {    NSHTTPURLResponse *httpResp =    (NSHTTPURLResponse*) response;    //状态码为200请求成功    if (httpResp.statusCode == 200) {     //返回的数据类型为json数组,解析     NSError *jsonError;     NSArray *filesJSON = [NSJSONSerialization            JSONObjectWithData:data            options:NSJSONReadingAllowFragments            error:&jsonError];     NSMutableArray *dbFiles =     [[NSMutableArray alloc] init];     if (!jsonError) {      for (NSDictionary *fileMetadata in        filesJSON) {       DBFile *file = [[DBFile alloc]           initWithJSONData:fileMetadata];       [dbFiles addObject:file];      }      [dbFiles sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {       return [obj1 compare:obj2];      }];      //添加到数组中保存      _photoThumbnails = dbFiles;      //更新主界面      dispatch_async(dispatch_get_main_queue(), ^{       [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];       [self.tableView reloadData];      });     }    } else {     // 处理相应失败//    }   } else {    // 这里处理失败 //   }  }] resume]; } refreshPhotos: 

4.休息一下,运行看看,应该可以看到图片加载了,我的效果如图:

NSURLSession使用实战教程

好了,现在我做上传图片的操作,并通过NSURLSessionDelegate和NSURLSessionTaskDelegate来帮助我们了解上传的状态和进度。

5.修改PhotosViewController.m使其遵守NSURLSessionTaskDelegate代理协议,代码如图:

@interface PhotosViewController ()<UITableViewDelegate, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate, NSURLSessionTaskDelegate>

6.添加如下属性,用于保存上传任务:

@property (nonatomic, strong) NSURLSessionUploadTask *uploadTask;

7.修改uploadImage:方法,替代为如下内容:

- (void)uploadImage:(UIImage*)image {  NSData *imageData = UIImageJPEGRepresentation(image, 0.6);  // 配置一次只能对服务器一个连接  NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];  config.HTTPMaximumConnectionsPerHost = 1;  [config setHTTPAdditionalHeaders:@{@"Authorization": [Dropbox apiAuthorizationHeader]}];  // 初始化上传会话  NSURLSession *upLoadSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];  // 上传任务的URL地址  NSURL *url = [Dropbox createPhotoUploadURL];  NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];  [request setHTTPMethod:@"PUT"];  // 设置上传的图片数据  self.uploadTask = [upLoadSession uploadTaskWithRequest:request fromData:imageData];  // 上传进度  self.uploadView.hidden = NO;  [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];  // 启动任务  [_uploadTask resume]; } uploadImage: 

8.最类最后面添加NSURLSessionTaskDelegate的两个方法实现:

#pragma mark - NSURLSessionTaskDelegate 方法  // /**  *这个代理方法可以跟踪进度  *  *  @param session                  会话  *  @param task                     任务  *  @param bytesSent                正在接受到的数据大小  *  @param totalBytesSent           总的接受的数据带大小  *  @param totalBytesExpectedToSend 估算总共需要接受的数据大小  */ - (void)URLSession:(NSURLSession *)session               task:(NSURLSessionTask *)task    didSendBodyData:(int64_t)bytesSent     totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {     //根据接收到数据大小和总的数据大小计算出进度条显示的进度值     dispatch_async(dispatch_get_main_queue(), ^{         [_progress setProgress:          (double)totalBytesSent /          (double)totalBytesExpectedToSend animated:YES];     }); }  /**  *当上传或下载数据成功时执行  *  *  @param session 会话  *  @param task    任务  *  @param error   错误  */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {     // 主线程更新进度,隐藏加载提示,隐藏上传进度     dispatch_async(dispatch_get_main_queue(), ^{         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];         _uploadView.hidden = YES;         [_progress setProgress:0.5];     });          if (!error) {         // 2         dispatch_async(dispatch_get_main_queue(), ^{             [self refreshPhotos];         });     } else {         //处理错误     } }  NSURLSessionTaskDelegate 方法

9.找到cancelUpload:方法,当我们点击cancel取消时会调用此方法,这里我们需要取消上传任务:

// 停止上传 - (IBAction)cancelUpload:(id)sender {     //取消上传任务     if (_uploadTask.state == NSURLSessionTaskStateRunning) {         [_uploadTask cancel];     }  }

10.哈哈,大功告成,运行一下看看效果吧

NSURLSession使用实战教程

至此我通过三篇文章介绍了NSULRSession套件的原理,和它的常用类,然后通过一个实战例子告诉大家NSURLSession如何使用。谢谢大家!

正文到此结束
Loading...