iOS URL Loading System 简介

1 总览

URL Loading System 提供获取资源的能力通过URLs,使用标准https协议或者是开发者自定义的协议。使用标准Internet协议与URL交互并与服务器通信。且加载执行的过程是异步的,保证了应用界面响应与处理加载数据的同时执行。

您可以使用一个URLSession实例来创建一个或多个URLSessionTask实例,这些实例可以将数据从网络获取并返回到您的应用,下载文件或将数据和文件上传到远程位置。要配置会话,请使用 URLSessionConfiguration对象控制Task的行为,例如如何使用缓存和cookie,或者是否允许在蜂窝网络上进行连接等配置。

您可以重复使用一个session来创建多个Task。例如,一个Web浏览器可能具有用于常规浏览和私有浏览的单独会话,其中私有会话不缓存其数据。图1显示了具有这些配置的两个会话如何创建多个任务。
在这里插入图片描述
每个Session都与一个代理委托Delegate相关联,以接收定期更新(或错误)。默认委托调用您定义的completion handler block。

您可以将会话配置为在后台运行,以便在应用程序挂起时,系统可以下载数据并唤醒应用程序以提供结果。

这项技术的核心在于基于 NSURL 这个类来访问资源,除了加载 URL 的类 NSURLSession 之外,我们把其他相关辅助类分为 5 类(如图所示):

  • 协议支持(protocol support)
  • 认证和证书(authentication and credentials)
  • cookie 存储(cookie storage)
  • 请求配置(configuration management)
  • 缓存管理(cache management)
    在这里插入图片描述

2 URL Loading 能力

URL Loading System 最常用的类就是根据 URL 请求数据的URLSession。URLSession有一个shared基本请求的单例会话(没有配置对象)。它可以作为一个很好的起点。您可以通过调用共享类方法来使用会话。对于其他类型的会话,您可以URLSession使用以下三种配置之一创建一个。URLSession 的 API 支持三种类型的 session:

  • Default sessions:其行为跟 Foundation 中提供的其他 URL 加载方式类似,支持磁盘缓存,并且会把凭证(credentials)保存到 keychain 中。
  • Ephemeral sessions:不会存储任何数据到磁盘上,所有的缓存、凭证都只会随着 session 保存在内存中,而且一旦这个 session 被清除了,这些缓存同时也会被清除。
  • Background sessions:跟 default sessions 的行为基本类似,提供数据上传与下载能力,但是还支持后台驻留进程的数据传输,也就是说,当 app 被挂起时,还可以在后台继续传输数据。

在会话中,您创建的任务可以有选择地将数据上载到服务器,然后以磁盘上的文件或NSData内存中的一个或多个对象的形式从服务器检索数据。该URLSessionAPI提供了四种类型的任务:

  • 数据任务使用NSData对象发送和接收数据。数据任务旨在向服务器发出简短的,经常是交互式的请求Request。
  • 上载任务与数据任务相似,但是它们还发送数据(通常以文件形式),并在应用程序不运行时支持后台上传。
  • 下载任务以文件形式检索数据,并在应用程序不运行时支持后台下载和上传。
  • WebSocket任务使用RFC 6455中定义的WebSocket协议通过TCP和TLS交换消息。

3 URLRequest 请求与响应

URLRequest封装了加载请求的两个基本属性:要加载的URL和用于加载该请求的策略。此外,对于HTTP和HTTPS请求,URLRequest包括HTTP方法(GET,POST等)和HTTP请求头等。

其实GET和POST差不多是一样的,一般我们认为GET方法是不用上传参数的,所有的参数都在请求链接中或者索性直接没参数,在这里GET方法中你看到参数是可以放在请求体中的,而大多数搜索到的区别都认为GET的参数在请求链接上,这种说法是不对的,GET参数完全可以放在请求体中,并不是因为GET连接长度有限制,起到限制的不是http协议,而是服务器和浏览器,所以乍一看这里GET和POST几乎一样,但它们在HTTPMethod是有着本质区别的。

另外需要注意的是,这里的字典,如果你想封装的话一定要考虑字典为空的情况,如果为空,请不要使用 request.HTTPMethod = “XXX”,否则会报错,所以,如你所见,当字典为空时,POST和GET没有任何区别。

在Swift中,也存在一些封装的比较好的网络请求库例如Alamofire,不过下面的示例展示的是原生的请求方法,下面看代码:

3-1 GET 请求示例

 	/**
     GET请求
     */
    func GETACtion() {
        //请求URL
        let url:NSURL! = NSURL(string: "http://iappfree.candou.com:8080/free/applications/limited")
        let request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
        let list  = NSMutableArray()
        var paramDic = [String: String]()

        if paramDic.count > 0 {
            //设置为GET请求
            request.HTTPMethod = "GET"
            //拆分字典,subDic是其中一项,将key与value变成字符串
            for subDic in paramDic {
                let tmpStr = "\(subDic.0)=\(subDic.1)"
                list.addObject(tmpStr)
            }
            //用&拼接变成字符串的字典各项
            let paramStr = list.componentsJoinedByString("&")
            //UTF8转码,防止汉字符号引起的非法网址
            let paraData = paramStr.dataUsingEncoding(NSUTF8StringEncoding)
            //设置请求体
            request.HTTPBody = paraData
        }
        //默认session配置
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: config)
        //发起请求
        let dataTask = session.dataTaskWithRequest(request) { (data, response, error) in

            //            let str:String! = String(data: data!, encoding: NSUTF8StringEncoding)
            //            print("str:\(str)")
            //转Json
            let jsonData:NSDictionary = try! NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! NSDictionary
            print(jsonData)
        }
        //请求开始
        dataTask.resume()
    }

3-2 POST 请求示例

	/**
     POST请求
     */
    func POSTACtion() {
        //请求URL
        let url:NSURL! = NSURL(string: "http://iappfree.candou.com:8080/free/applications/limited")
        let request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
        let list  = NSMutableArray()
        var paramDic = [String: String]()

        if paramDic.count > 0 {
            //设置为POST请求
            request.HTTPMethod = "POST"
            //拆分字典,subDic是其中一项,将key与value变成字符串
            for subDic in paramDic {
                let tmpStr = "\(subDic.0)=\(subDic.1)"
                list.addObject(tmpStr)
            }
            //用&拼接变成字符串的字典各项
            let paramStr = list.componentsJoinedByString("&")
            //UTF8转码,防止汉字符号引起的非法网址
            let paraData = paramStr.dataUsingEncoding(NSUTF8StringEncoding)
            //设置请求体
            request.HTTPBody = paraData
        }
        //默认session配置
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: config)
        //发起请求
        let dataTask = session.dataTaskWithRequest(request) { (data, response, error) in

            //            let str:String! = String(data: data!, encoding: NSUTF8StringEncoding)
            //            print("str:\(str)")
            //转Json
            let jsonData:NSDictionary = try! NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! NSDictionary

            print(jsonData)

        }
        //请求开始
        dataTask.resume()
    }

4 URLSession DataTask 的使用

会话建立了连接,但是它不执行任何任务。要下载或上传数据,我们必须创建一个具有特定任务的URLSessionTask对象,并将其添加到会话中。 URLSession类包含以下方法来创建任务以下载数据以及下载或上传文件。

dataTask(with: URL, completionHandler: Block)
此方法创建一个任务来下载数据并将其添加到会话中。 with属性是带有数据位置的URL,completionHandler属性是在任务完成时执行的闭包。闭包接受三个可选属性,一个具有请求返回的数据的Data结构,一个具有请求状态的URLResponse对象,以及一个Error值,用于指示是否发生错误。此方法还有一个其他实现方法,它采用URLRequest对象而不是URL结构。

downloadTask(with: URL, completionHandler: Block)
此方法创建一个任务来下载文件并将其添加到会话中。 with属性是具有文件位置的URL,completionHandler属性是在任务完成时执行的闭包。该闭包接收三个可选属性,一个URL结构(带有一个表示下载的文件的临时保存位置),一个URLResponse对象(带有请求的状态)以及一个Error值,用于指示是否发生错误。此方法还有一个附加实现,它采用URLRequest对象而不是URL结构。

uploadTask(with: URLRequest, from: Data?, completionHandler: Block)
此方法创建一个任务来上传文件并将其添加到会话中。 with属性是一个带有URL的请求,是我们上传文件的网络位置,from属性是一个Data结构,其中包含我们要用于创建文件的数据,而completionHandler属性是一个闭包,当任务被执行时执行。完成。该闭包接收三个可选属性,一个带有服务器返回的数据的Data结构,一个带有请求状态的URLResponse对象,以及一个Error值,该值指示是否发生错误。

uploadTask(with: URLRequest, fromFile: URL, completionHandler: Block)
此方法创建一个任务来上传文件并将其添加到会话中。 with属性是一个URLRequest对象,其中包含我们要在其中上传文件的URL,fromFile属性是一个URL结构,其中包含了我们要上传文件的位置,而complementHandler属性是一个闭包,当任务完成式时执行。该闭包接收三个可选属性,一个带有服务器返回的数据的Data结构,一个带有请求状态的URLResponse对象,以及一个Error值,该值指示是否发生错误。

这些方法返回的对象是URLSessionTask类的子类(URLSessionDataTask,URLSessionDownloadTask和URLSessionUploadTask)。所有这些子类都包含以下方法来用于控制任务。

cancel()—用于取消任务.
resume()—用于启动或恢复任务.
suspend()—用于挂起任务直至任务被再次启动

任务完成后,它将结果发送到闭包进行处理。 例如,如果我们使用dataTask() 方法从网站获取数据,则此任务的闭包将接收带有数据的值和带有请求状态的URLResponse类型的对象。 当我们使用HTTP协议访问URL时,响应由类型为HTTPURLResponse(URLResponse的子类)的对象表示。 此类包含statusCode属性,以返回确定请求状态的代码。 有多个值可用于确定诸如请求是否成功(200)或其他错误的情况。 如果只需要确保正确下载了数据,就必须在处理任何内容之前检查statusCode属性的值是否等于200。 以下示例显示了如何执行基本请求。

4-1 DataTask 示例一

import UIKit
class ViewController: UIViewController { override func viewDidLoad() {
super.viewDidLoad()
let webURL = URL(string: “https://www.yahoo.com”)
let request = URLRequest(url: webURL!)
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { 
	(data, response, error) in
 	if error == nil && data != nil {
	if let resp = response as? HTTPURLResponse {
		let status = resp.statusCode if status == 200 {
		let content = String(data: data!, encoding: 
		String.Encoding.ascii) print(content!)
		} else { print(Error)
		} }
	} else { print(Error) }
	})
task.resume()
} }

4-2 DataTask 示例二

import UIKit
import PlaygroundSupport

let configuration = URLSessionConfiguration.default
configuration.allowsCellularAccess
configuration.waitsForConnectivity = true
let session = URLSession(configuration: configuration)

let url = URL(string: "https://jsonplaceholder.typicode.com/users")!

let task = session.dataTask(with: url) {
    (data, response, error) in
    guard let httpResponse = response as? HTTPURLResponse,
        httpResponse.statusCode == 200 else {
            return
    }
    guard let data = data else {
        print(error.debugDescription)
        return
    }
    if let result = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as String? {
        print(result)
    }
    PlaygroundPage.current.finishExecution()
}

task.resume()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值