在iOS9.0之后,以前使用的NSURLConnection过期,苹果推荐使用NSURLSession来替换NSURLConnection完成网路请求相关操作。
NSURLSessionConfiguration类对网络请求层的设置选项进行了扩充,可以配置从指定可用网络,到 cookie,安全性,缓存策略,再到使用自定义协议,启动事件的设置,以及用于移动设备优化的几个新属性。本例只对常规的网络请求作演示,不涉及NSURLSessionConfiguration配置。
NSURLSession的使用非常简单,先根据会话对象创建一个请求Task,然后执行该Task即可。
本文介绍内容包括常规数据请求(GET/POST)、数据下载上传以及断点续传,仅贴出代码供参考。
//GET方式(系统默认)
NSString *urlString = @"http://kaiyi.3tichina.com:8001/mall/list.php?page=1&catid=4";
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@",dic);
}else{
NSLog(@"Error: %@",error.localizedDescription);
}
}];
[task resume];
//POST方式
NSString *urlString = @"http://kaiyi.3tichina.com:8001/mall/list.php";
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
[request setHTTPMethod:@"POST"];
NSString *httpBodyString = @"page=1&catid=4";
[request setHTTPBody:[httpBodyString dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@",dic);
}else{
NSLog(@"Error: %@",error.localizedDescription);
}
}];
[task resume];
NSString *urlString = @"";/*目标服务器url*/
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:[NSData dataWithContentsOfFile:@""]/*要上传的数据*/ completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
//TODO: 上传成功的操作
}else{
NSLog(@"error: %@",error.localizedDescription);
}
}];
[uploadTask resume];
NSString *urlString = @"https://www.google.com.hk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png";
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
//将下载的临时数据移到本地持久化
NSString *docpath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [docpath stringByAppendingPathComponent:[[response URL] lastPathComponent]];
NSFileManager *fm = [NSFileManager defaultManager];
[fm moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
//TODO: 更新UI
self.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfFile:filePath]];
});
}else{
NSLog(@"error: %@",error.localizedDescription);
}
}];
[downloadTask resume];
演示demo界面如下:
@interface BreakpointContinuinglyViewController ()<NSURLSessionDownloadDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progress;
@property (weak, nonatomic) IBOutlet UILabel *labelStatus;
@property (weak, nonatomic) IBOutlet UIButton *buttonDownload;
@property (weak, nonatomic) IBOutlet UIButton *buttonSuspend;
@property (weak, nonatomic) IBOutlet UIButton *buttonResume;
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
@property (nonatomic, strong) NSData *resumeData; //已下载数据
@end
@implementation BreakpointContinuinglyViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self loadBaseUI];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)loadBaseUI{
self.progress.progress = 0.f;
}
- (IBAction)actionStartDownload:(id)sender {
NSString *sourcePath = @"http://www.1ting.com/api/audio?/i/1031/7/14.mp3";
NSURL *url = [NSURL URLWithString:sourcePath];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.f];
/*! 第一个参数为session的配置信息,[NSURLSessionConfiguration defaultSessionConfiguration]为默认配置 第二个参数为代理对象 第三个参数为队列,决定执行代理方法的线程。可以是主队列和自定义队列: 主队列[NSOperationQueue mainQueue] 下载完毕后如果需要根据下载数据操作UI通常放入主队列 自定义队列[NSOperationQueue alloc]init] 下载完毕后如果只是数据的操作通常放入自定义队列 */
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
_downloadTask = [_session downloadTaskWithRequest:request];
[_downloadTask resume];
self.labelStatus.text = @"连接中...";
}
- (IBAction)actionSuspend:(id)sender {
/*暂停任务 暂停后超时计数器开始运作,若规定时间内没有resume任务,task运行失败,回调URLSession:task:didCompleteWithError:方法,通过该方法中的error参数可以获取到已经下载的数据 */
[_downloadTask suspend];
self.labelStatus.text = @"暂停下载";
}
- (IBAction)actionResume:(id)sender {
if (self.resumeData) {
//断点续传
_downloadTask = [_session downloadTaskWithResumeData:self.resumeData];
}
/*task在3中状态下都可以resume 1.task未开始,通过resume开始执行 2.task暂停,通过resume继续执行 3.通过已下载的数据继续执行task(断点续传) */
[_downloadTask resume];
self.labelStatus.text = @"等待继续下载";
}
- (IBAction)actionCancel:(id)sender {
/*取消任务 暂停中的task同样可以取消 取消后立即回调URLSession:task:didCompleteWithError:方法。 */
[_downloadTask cancel];
// [_downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
// //因注意不要重复获取已下载的数据(要么这里获取,要么URLSession:task:didCompleteWithError:方法了获取)
// self.resumeData = [NSData dataWithData:resumeData];
// }];
self.labelStatus.text = @"任务已取消";
}
#pragma mark - NSURLSessionDownloadDelegate
//监听下载过程
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
//TODO: 判断是否连接成功
self.labelStatus.text = @"下载进行中";
self.progress.progress = (float)totalBytesWritten/totalBytesExpectedToWrite;
}
//完成下载(下载成功)
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [docPath stringByAppendingPathComponent:@"14.mp3"];
NSFileManager *fm = [NSFileManager defaultManager];
[fm moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
self.labelStatus.text = @"下载完成 数据已保存到本地";
}
//开始断点续传时调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
NSLog(@"断点续传:%lld",fileOffset);
}
#pragma mark - NSURLSessionTaskDelegate
//任务执行结束时调用,可能成功可能失败。若失败,可以通过NSURLSessionDownloadTaskResumeData作为key从error的userinfo中获取到已经下载的数据
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error{
if (error) {
self.resumeData = [error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData];
NSLog(@"%@",error.localizedDescription);
self.labelStatus.text = @"下载失败,数据已保存";
}
}
@end
欢迎大家指错或者提出宝贵意见!!
参考文档:
《从 NSURLConnection 到 NSURLSession》
《NSURLSession 教程》
《iOS开发网络篇—发送GET和POST请求(使用NSURLSession)》