我们者知道将代码分成单独的组件是最佳做法,每个组件都致力于单个任务。我们在使用SwiftUI对网络API请求是通过是按http://www.55mx.com/ios/179.html这个方法做的。在小型项目里完全没有问题,如果我们需要多个接口的语法,总是会复制修改,我把这些重复的工作包装成一个可以重复利用的组件。
在网上找了一圈,看有没有造好的轮子,直接拿过来就用,目前还没有找到更好的。索性自己来造这个轮子吧。我将组件分成了3个部分来处理:
1、使用URLSession请求API。-> 2、使用JSONDecoder解码。 -> 3、将解码成功的数据返回给闭包处理。
通过扩展URLSession第1步非常的容易搞定,但如果要使用将第2、3步组合在一下,就喜欢使用Result和Decodable组合完成。
URLSession既是处理基于HTTP和HTTPS请求的类(Class),主要任务是负责发送和接收请求的关键对象。我们可以通过URLSessionConfiguration创建它,有下面三种配置风格:
let defaultSession = URLSession(configuration: .default)//默认会话配置对象。
let ephemeralSession = URLSession(configuration: .ephemeral)//不使用持久存储缓存、cookie或凭据的会话配置。
var urlSession: URLSession = {
let config = URLSessionConfiguration.background(withIdentifier: "MySession")
config.isDiscretionary = true
config.sessionSendsLaunchEvents = true
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
URLSessionConfiguration还可以配置会话属性,如:超时值、缓存策略和HTTP标头。有关配置选项的完整列表,请参阅苹果的文档。
上面介绍了会话的请求配置,接着就是会话的任务URLSessionTask,它也是一个抽象类,表示任务对象。会话创建一个或多个任务来完成获取数据和下载或上传文件的实际工作。它也有三种类型的具体会话任务:
//通用的URLSession,可同时处理多个任务
class TaskManager {
static let shared = TaskManager()
let session = URLSession(configuration: .default)//使用默认配置
typealias completionHandler = (Data?, URLResponse?, Error?) -> Void//抓取完成后的处理闭包函数
var tasks = [URL: [completionHandler]]()//多个抓取任务数组
func dataTask(with url: URL, completion: @escaping completionHandler) {
if tasks.keys.contains(url) {
tasks[url]?.append(completion)
} else {
tasks[url] = [completion]
let _ = session.dataTask(with: url, completionHandler: { [weak self] (data, response, error) in
DispatchQueue.main.async {
print("抓取数据完成:(url.description)")
guard let completionHandlers = self?.tasks[url] else { return }
for handler in completionHandlers {
print("闭包处于完成")
handler(data, response, error)
}
}
}).resume()
}
}
}
使用方式(实例化):
let url = URL(string: "API的地址")
TaskManager.shared.dataTask(with: url) { (data, response, error) in
if let data = data{
do{
let response = try JSONDecoder().decode(Model.self, from: data)
print(response)//处理获取到的数据
}catch{
print("解析JSON出错啦~:",error.localizedDescription)
}
}
}
import Foundation
extension URLSession {
func perform(_ request: URLRequest, decode decodable: T.Type, result: @escaping (Result<T, Error>) -> Void) {
//第1步:使用URLSession请求API。
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return } // 空数据
if let error = error {
result(.failure(error))//网络错误
}
//第2步:使用JSONDecoder解码。
do {
let object = try JSONDecoder().decode(decodable.self, from: data)
DispatchQueue.main.async {
result(.success(object))//第3步:、将解码成功的数据返回给闭包处理。
}
}catch let DecodingError.dataCorrupted(context) {
print("在解码值时发生的错误:(context)")
} catch let DecodingError.keyNotFound(key, context){
print("健:'(key)'未找到:", context.debugDescription)
print("编码路径(codingPath):", context.codingPath)
} catch let DecodingError.valueNotFound(value, context){
print("值:'(value)'未找到:", context.debugDescription)
print("编码路径(codingPath):", context.codingPath)
} catch let DecodingError.typeMismatch(type, context){
print("类型:'(type)'不匹配:", context.debugDescription)
print("编码路径(codingPath):", context.codingPath)
}catch{
#if DEBUG
print("解析JSON出错啦~:",error.localizedDescription)
#endif
result(.failure(error))
}
}.resume()
}
}
上面我们通过扩展URLSession得到了URLSession.shared.perform方法,让请求、解码、将解码结果交给闭包处理。
使用方法(实例化):
let url = URL(string: "API地址")!
let request = URLRequest(url: url)
//这里不能直接使用 URLSession.perform 原因我暂时不知道,有知道的同学请发表评论,谢谢!
URLSession.shared.perform(request, decode: Model.self) { result in
switch result {
case .failure(let error):
print("出错了:",error)
case .success(let object):
print("返回结果:",object)
}
}
上面扩展后的perform最后的闭包是在主线程里完成的。所以我们在实例化的时候 可以不用重复处理线程问题。
如果你对抓取API有入门学习的需求,请参考:http://www.55mx.com/ios/179.html 与URLSession异步处理:http://www.55mx.com/ios/177.html
除非注明,网络人的文章均为原创,转载请以链接形式标明本文地址:https://www.55mx.com/post/188
《【APP开发】扩展URLSession并使用Result和Decodable包装一个访问远程API的组件》的网友评论(1)