75142913在线留言
【APP开发】扩展URLSession并使用Result和Decodable包装一个访问远程API的组件_IOS开发_网络人

【APP开发】扩展URLSession并使用Result和Decodable包装一个访问远程API的组件

Kwok 发表于:2021-12-26 13:26:22 点击:43 评论: 1

我们者知道将代码分成单独的组件是最佳做法,每个组件都致力于单个任务。我们在使用SwiftUI对网络API请求是通过是按http://www.55mx.com/ios/179.html这个方法做的。在小型项目里完全没有问题,如果我们需要多个接口的语法,总是会复制修改,我把这些重复的工作包装成一个可以重复利用的组件

在网上找了一圈,看有没有造好的轮子,直接拿过来就用,目前还没有找到更好的。索性自己来造这个轮子吧。我将组件分成了3个部分来处理:

1、使用URLSession请求API。-> 2、使用JSONDecoder解码。 -> 3、将解码成功的数据返回给闭包处理。

通过扩展URLSession第1步非常的容易搞定,但如果要使用将第2、3步组合在一下,就喜欢使用Result和Decodable组合完成。

一、URLSession概述

URLSession既是处理基于HTTP和HTTPS请求的类(Class),主要任务是负责发送和接收请求的关键对象。我们可以通过URLSessionConfiguration创建它,有下面三种配置风格:

  • 默认:创建一个默认配置对象,该对象使用磁盘持久全局缓存、凭据和cookie存储对象。
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标头。有关配置选项的完整列表,请参阅苹果的文档。 

APP开发扩展URLSession并使用Result和Decodable包装一个访问远程API的组件

上面介绍了会话的请求配置,接着就是会话的任务URLSessionTask,它也是一个抽象类,表示任务对象。会话创建一个或多个任务来完成获取数据和下载或上传文件的实际工作。它也有三种类型的具体会话任务:

  • URLSessionDataTask:使用此任务处理GET请求,以从服务器检索到内存的数据。
  • URLSessionUploadTask:使用此任务通过POST或PUT方法将文件从磁盘上传到Web服务。
  • URLSessionDownloadTask:使用此任务将文件从远程服务下载到临时文件位置。

APP开发扩展URLSession并使用Result和Decodable包装一个访问远程API的组件

二、通过URLSession同时处理多个任务

//通用的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)
        }
    }
    
}

三、扩展URLSession实现可通用的的API组件

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
标签:请求URLSession组件Kwok最后编辑于:2021-12-26 17:26:11
1
感谢打赏!

《【APP开发】扩展URLSession并使用Result和Decodable包装一个访问远程API的组件》的网友评论(1)

  1. #1URLSession在实例化的时候,内部有一个static let shared = URLSession(),所以我们需要通过URLSession.shared.perform()直接调用,所以不能直接使用 URLSession.perform(),除非再重新实例化一次URLSession。
    admin于2年前(2021-12-27)发表。(0)(0)

本站推荐阅读

热门点击文章