当前位置:首页 > 综合热点 > 正文

IOS笔记:网络请求之 NSURLSession

摘要: IOS笔记:网络请求之NSURLSessionIOS笔记:网络请求之NSURLSessionNSURLSession在iOS7....
IOS笔记:网络请求之 NSURLSession

IOS笔记:网络请求之 NSURLSession

NSURLSession在iOS7.0时被Apple提出后,虽然Apple一直对其良好的API设计大力推广。在下面我们将会知道:

IOS笔记:网络请求之 NSURLSession


我们为什么从NSURLConnection转移为NSURLSession?NSURLSession 的基本用法AFN对NSURLSession的封装2015年5月RFC 7540正式发表的下一代HTTP协议,是1999年来HTTP 1.1发布后的首个更新。相对于前一个版本,HTTP /2以快著称。如下图,对相同图片、相同服务器的下载,在不同协议下所需的时间:
ios9之后,NSURLSession开始支持HTTP/2HTTP/2 意味着更快的网络连接速度在苹果官方文档中,对属性** HTTPMaximumConnectionsPerHost**这样描述
DiscussionThis property determines the maximum number of simultaneous connections made to each host by tasks within sessions based on this configuration.This limit is per session, so if you use multiple sessions, your app as a whole may exceed this limit. Additionally, depending on your connection to the Internet, a session may use a lower limit than the one you specify.The default value is 6 in macOS, or 4 in iOS.```此属性根据此配置确定会话中的任务对每个主机的最大并发连接数,MacOS中的默认值为6,iOS中的默认值为4。先看一个最简单的网络请求
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:URLString]];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
[dataTask resume];
NSRULSession 网络请求系统包括3方面:session、configuration、Task#### NSURLSession我们从头文件入手
// 初始化Session@property (class, readonly, strong) NSURLSession *sharedSession;
(NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;(NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;// 属性及代理@property (readonly, retain) NSOperationQueue *delegateQueue;@property (nullable, readonly, retain) id <NSURLSessionDelegate> delegate;@property (readonly, copy) NSURLSessionConfiguration *configuration;
// dataTask
(NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;(NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;// uploadTask
(NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;(NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;(NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;// downloadTask
(NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;(NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;(NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
苹果也为NSURLSession 配置了block的回调版本
// NSURLSessionDataTask
(NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;(NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;// NSURLSessionUploadTask
(NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;(NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(nullable NSData *)bodyData completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;// NSURLSessionDownloadTask
(NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;(NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;(NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
如何使用? 我们从网络加载一张图片
NSURLSession *session = [NSURLSession sharedSession];NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:ImageURL]];NSURLSessionDataTask *sessionDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {    UIImage *image = [[UIImage alloc]initWithData:data];}];[sessionDataTask resume];
### NSURLSessionConfiguration
// 默认的配置 , 缓存存储在磁盘上@property (class, readonly, strong) NSURLSessionConfiguration *defaultSessionConfiguration;// 不会创建持久性存储的缓存.@property (class, readonly, strong) NSURLSessionConfiguration *ephemeralSessionConfiguration;// 允许程序在后台进行上传下载工作
(NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier;// 配置缓存策略@property NSURLRequestCachePolicy requestCachePolicy;// 请求超时@property NSTimeInterval timeoutIntervalForRequest;// 添加请求头@property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;
#### NSURLSessionTask苹果并没有公开让我们使用 NSURLSessionTask ,而是只能使用它的子类 : 1. NSURLSessionDataTask 一般网络请求2. NSURLSessionDownloadTask 下载大型文件等3. NSURLSessionUploadTask 处理上传请求,图片或文件Task Delegate 继承关系
NSURLSessionTask (抽象类,NSURLSessionTaskDelegate)
NSURLSessionDataTask (NSURLSessionDataDelegate)
NSURLSessionUploadTask (NSURLSessionDataDelegate)
NSURLSessionDownloadTask (NSURLSessionDownloadDelegate)NSURLSessionDelegate
NSURLSessionTaskDelegate
NSURLSessionDataDelegate
NSURLSessionDownloadDelegate
在初始化时,我们只需指定对应的delegate,这个手机避免了多次设计delegate,根据不同的task实现不同的delegate。从头文件看各个delegate的作用
// NSURLSessionDelegate//当一个session遇到系统错误或者未检测到的错误
(void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error//当请求需要认证、或者https证书认证(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challengecompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;//如果应用进入后台、这个方法会被调用。我们在这里可以对session发起的请求做各种操作。(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session ;// NSURLSessionTaskDelegate/*当请求重定向的时候调用这个方法。我们必须设置一个新的NSURLRequest对象传入completionHandler来重定向新的请求,但是当session是background模式的时候,这个方法不会被调用。 */
(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)taskwillPerformHTTPRedirection:(NSHTTPURLResponse )responsenewRequest:(NSURLRequest )requestcompletionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler;/当请求需要认证的时候调用这个方法。如果没有实现这个代理,那么请求认证这个过程不会被调用。/(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)taskdidReceiveChallenge:(NSURLAuthenticationChallenge )challengecompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;/ 如果请求需要一个新的请求体时,这个方法就会被调用。比如认证失败的时候,我们可以通过这个机会从新认证。 */(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask )taskneedNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler;/当我们上传数据的时候,我们可以通过这个代理方法获取上传进度。 */(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask )taskdidSendBodyData:(int64_t)bytesSenttotalBytesSent:(int64_t)totalBytesSenttotalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;/ 当task的统计信息收集好了以后,调用这个方法。 */(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask )task didFinishCollectingMetrics:(NSURLSessionTaskMetrics )metrics;/当一个task出错的时候,会调用这个方法。如果error是nil,也会调用这个方法,表示task完成。/(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)taskdidCompleteWithError:(nullable NSError *)error;// NSURLSessionDownloadDelegate/当一个下载task任务完成以后/
(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTaskdidFinishDownloadingToURL:(NSURL )location;/获取下载进度 */(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask )downloadTaskdidWriteData:(int64_t)bytesWrittentotalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;/重启一个下载任务。如果下载出错,NSURLSessionDownloadTaskResumeData里面包含重新开始下载的数据。 */(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTaskdidResumeAtOffset:(int64_t)fileOffsetexpectedTotalBytes:(int64_t)expectedTotalBytes;
参考文献[AFNetWorking 的核心 AFURLSessionManager](https://github.com/Draveness/Analyze/blob/master/contents/AFNetworking/AFNetworking%20%E7%9A%84%E6%A0%B8%E5%BF%83%20AFURLSessionManager%EF%BC%88%E4%BA%8C%EF%BC%89.md#NSURLSession)[AFNetWorking 源码之 AFURLSessionManager](https://juejin.im/entry/58f6d0be0ce463006bc9d919)

Swift3.0服务端开发(五) 记事本开发 iOS+服务端

前边以及陆陆续续的介绍了使用Swift3.0开发的服务端应用程序的Perfect框架。本篇博客就做一个阶段性的总结,做一个完整的实例,其实这个实例在《Swift3.0服务端开发(一)》这篇博客中已经简单的介绍过了,本篇博客就来详细的聊一下这个工程的具体实现细节。当然包括iOS端和服务端的代码。本篇博客的介绍顺序按照功能模块来划分的,如登录注册模块、记事本列表,记事本的增删改查等功能。在每个功能模块,我们先给出服务端代码的实现,然后给出客户端代码的实现。
本篇博客的前几部分主要介绍整个工程的公用模块,为工程的实现做准备,下方就是我们今天博客要做的东西。本篇博客iOS端的网络请求主要使用的NSURLSession来实现的,关于URLSession更详细的介绍请参考之前发布的博客。

一、记事本数据库的设计
数据库的设计以及数据库表的创建我都使用Sequel Pro来实现的,关于Sequel Pro的使用请看上篇博客的介绍,本篇博客关于Sequel Pro的介绍就不做过多赘述了。首先我们先给出记事本数据库表的设计,以备使用。我们先创建一个名为perfect_note的数据库(步骤略),然后再创建相应的数据库表。因为我们的记事本比较简单,主要包括登录、注册以及记事本的增删改查。所以我们的数据库结构也是比较简单的,perfect_note数据库中只有两个表,一个是user表,一个是content表,下方会给出详细的介绍过程。
1.user表的创建
首先我们来创建user表,user表负责存储用户信息,当用户注册和登录时都会操作这个表。注册用户时就是往该表中插入用户,登录时就是查询相应的用户信息。当然,为了Demo的简洁性,我们的user表中的字段也是比较少的。下方就是创建user表的SQL语句。其中有四个字段,主键id是整型而且是自增的,是用户的唯一表示。username字段存储的是用户名,password存储的就是用户密码。register_date存储的是用户注册时间,是时间戳,并且默认值是当前时间。
CREATE TABLE `user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(30) CHARACTER SET latin1 NOT NULL DEFAULT '', `password` varchar(30) CHARACTER SET latin1 NOT NULL DEFAULT '', `register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
2.content表的创建
创建完user表后,接下来就要创建我们的content表了。content表用来存储用户录入的笔记,下方就是content表的创建SQL语句。从下方的SQL语句中不难看出content表的字段包括自增的主键id,记录的标题title,记录的内容content,以及外键userID和创建时间create_time。
CREATE TABLE `content` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(30) CHARACTER SET gb2312 NOT NULL DEFAULT '', `content` text CHARACTER SET gb2312 NOT NULL, `userID` int(11) unsigned NOT NULL, `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `USER_FOREIGN_KEY` (`userID`), CONSTRAINT `USER_FOREIGN_KEY` FOREIGN KEY (`userID`) REFERENCES `user` (`id`)) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
二、iOS端基于NSURLSession网络请求类的封装
创建完数据库后,接下来我们来封装iOS端网络请求的共用代码。也就是说,iOS端的网络请求就会调用本部分封装的内容。当然本部分封装的网络请求类是使用NSURLSession类封装的。
1.字符串常量、闭包回调类型以及枚举的定义
首先我们先来定义一些封装网络请求类要使用的字符串常量以及枚举闭包回调。下方代码段做的就是这件事情,第一个框中定义了解析响应数据时使用到的字符串常量。“SUCCESS”表示请求成功,“FAILE”表示请求失败等等。
第二个框中定义的是三个闭包变量,用来将请求结果回调给调用者。RequestStart就是开始请求要调用的闭包类型,RequestSuccess则是请求成功后调用的闭包类型,RequestFailed则是请求失败要调用的闭包类型。这三者是请求类对外交流的桥梁。
第三个框则是请求方式的枚举,主要包括GET、POST、PUT、DELETE,当然还留了CUSTOM()自定义的扩展类型。在该枚举中的description计算属性负责将当前的枚举对象转换成其对于的字符串,具体如下所示:

2、网络请求基类的创建
接下来网络请求的基类,所有与网络请求相关的类都要继承自此类,下方的BaseRequest就是我们网络请求的基类。该类比较简单,主要声明了上面定义的三个闭包类型的变量,然后给出了相应的构造器。具体如下所示。

3.网络请求类的封装
接下来我们使用NSURLSession来封装我们的网络请求类,下方的Request类就是我们封装的网络请求类,该类继承自BaseRequest。下方是Request的部分代码,下方每个方法对应着GET、POST、PUT等请求,可以结合者REST一起使用。在每个具体请求的方法中会调用sessionDataTaskRequest()方法。会给这个方法传入不同的请求方式以及路径和参数。稍后我们会给出sessionDataTaskRequest()方法的具体实现,sessionDataTaskRequest()方法其中就使用了NSURLSession相关的内容发起了网络请求,具体请看下方对sessionDataTaskRequest()方法的详细介绍。

下方这个代码段就是sessionDataTaskRequest()方法的整体结构,首先我们根据函数的请求路径和参数拼接URL字符串,也就是第一个框中的部分。在该部分中的query()函数是将参数进行URL编码转换,这个函数是从AlamoFire框架中摘过来的。然后创建请求用的URLRequest对象。最后是创建Session对象发起DataTask任务了。当然请求的结果是在completionHandler闭包中进行处理,稍后会给出completionHandler闭包中的处理方式。

接着,我们给出请求成功后,对json数据的解析以及对返回结果的处理。下方就是completionHandler闭包中的代码片段。首先对服务器返回的json数据进行解析,解析后将json数据转换成对应的数据类型。然后根据响应报文的result字段来进行相应的操作。如果报文响应正常,就调用success()闭包,否则调用failure()闭包,如下所示:

至此我们iOS客户端的网络请求部分就封装完了,其他具体业务逻辑的网络请求调用上述的Request类即可,稍后会用到Request。
三、登录注册模块的开发
上面的基础工作完毕后,接下来我们就要来做我们相应的业务模块了。首先我们来进行登录注册模块的开发工作。 首先给出服务端相应模块的代码,然后在给出相应模块的iOS端的实现。关于Swift3.0连接和操作MySQL的详细内容请参考上一篇博客《Swift3.0服务端开发(四) MySQL数据库的连接与操作》,数据库的连接在本部分就不做过多赘述了。
1、服务端代码
(1)、登录或注册的第一步:接收用户名
下方代码是用户登录或者注册的第一步,通过用户名来查询用户信息,从而来判断该用户是否注册,如果未注册则去注册,如果注册过就去登录。如果查询成功,那么就将查询的用户ID和UserName返回给客户端。用户登录的代码和下方差不多,就是通过Select语句来匹配该用户名的密码是否与用户输入的一致,在此就不做过多赘述了。

(2)、用户注册
下方就是用户注册是调用的接口实现,主要是插入相应的用户信息,具体如下所示:

上面这些代码写完后,配置完相应的路由调用上述方法,我们的服务端代码就完成了。具体路由的配置因为篇幅有限,本篇博客就不做过多赘述了。
2、iOS客户端代码实现
接下来我们来实现iOS客户端的登录和注册的代码,下方就是登录或者注册的相关UI。用户输入用户后,点击下一步,会调用后台接口判断用户是否注册过,如果已注册输入密码登录,如果未注册就输入密码注册和登录。右边的UIViewController是共用的,两个页面,一个让用户输入用户名,一个则负责接收密码。UI比较简单,如下所示:

看完UI, 我们来看一下登录或注册的相关网络请求的代码。下方的UserInfoRequest类就负责所有与用户信息相关的网络请求,从下方的代码截图中,我们可以看到UserInfoRequest的基类是BaseRequest。下方的queryUserInfo(userName)就是上面左边的页面所调用的方法,用来判断该用户是否是注册过的用户。在queryUserInfo()中对Request类进行了实例化,并且调用了相应的请求方法。并且对相应的事件回调做了处理,具体如下所示。

在我们相应的ViewController中会调用上述的方法,下方就是用户在输入相应的用户信息后点击next所调用的方法。通过相应的闭包事件,最终将网络请求的结果回调到了VC中。

至此我们iOS客户端的登录就实现完毕了。 其他的代码和上面的思路类似,在此就不做过多赘述了。
本篇博客,就先到这儿吧,其他代码和上述的思路一直,按照上述的思路去实现笔记的增删改查即可,在此就不多废话了。完整Demo请移步github相关链接。
github分享链接: https://github.com/lizelu/PerfectDemo

发表评论