[Swift]封装一个网络工具Moya+HandyJSON

栏目: Swift · 发布时间: 5年前

内容简介:网络请求是 App 中最常用的更能之一,除了 Apple 提供的 URLSession 之外,还有对其进行封装,功能更加强的的 Alamofire等强大的工具,尽管这样,我们依然会在自己的 App 中封装一套网络请求工具,以达到做网络请求时,代码简洁高效.做网络请求的时候尽量的简单,只需要少量的代码即可处理返回的数据现在已经可以做网络请求了,比如:把百度首页数据请求下来

网络请求是 App 中最常用的更能之一,除了 Apple 提供的 URLSession 之外,还有对其进行封装,功能更加强的的 Alamofire等强大的工具,尽管这样,我们依然会在自己的 App 中封装一套网络请求工具,以达到做网络请求时,代码简洁高效.

封装目的:

做网络请求的时候尽量的简单,只需要少量的代码即可处理返回的数据

封装过程:

1.实现网络请求单例,提供可修改 baseURL和 get post 请求方法.

enum RequestMethod {
    case post
    case get
}
///请求对象 
struct Requset {
    init(method: RequestMethod = .post, baseURL: String = "", path: String, parameters: [String : Any]?){
        self.method = method
        self.baseURL = baseURL
        self.path = path
        self.parameters = parameters ?? [:]
    }
    var method: RequestMethod
    var baseURL: String
    var path: String
    var parameters: [String: Any]
}
///网络工具
class NetworkManager {
    static let shared = NetworkManager("")
    init(_ baseURL: String = "") {
        self.baseUrl = baseURL
    }
    var baseUrl = ""
    func post(path: String,params: [String: Any]?,result: @escaping ((Result<Data,Error>)->())){
        let request = Requset(baseURL:baseUrl, path: path, parameters: params)
        let target = Target(request: request)
        self.request(target: target, result: result)
    }
    func get(path: String,params: [String: Any]?,result: @escaping ((Result<Data,Error>)->())){
        let request = Requset(baseURL:baseUrl, path: path, parameters: params)
        let target = Target(request: request)
        self.request(target: target, result: result)
    }
    private func request(target: Target,result: @escaping ((Result<Data,Error>)->())) {
        MoyaProvider<Target>().request(target) { (res) in
            switch res {
            case .success(let a):
                result(.success(a.data))
            case .failure:
                result(.failure(res.error!))
            }
        }
    }
}
复制代码

现在已经可以做网络请求了,比如:把百度首页数据请求下来

NetworkManager("https://www.baidu.com").get(path: "", params: nil) { (res) in
    let data = try! res.get()
    print(String(data: data, encoding: .utf8))
}
复制代码

Optional("\r\n\r\n\r\n\r\n <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">\r\n <meta http-equiv="content-type" content="text/html;charset=utf-8">\r\n <meta content="always" name="referrer">\r\n <script src=" ss1.bdstatic.com/5eN1bjq8AAU… ">\r\n

页面不存在_百度搜索 \r\n ....

2. 二次封装

仅仅这样封装很明显不能够达到精简的目的,这样其实和直接使用 Alamofire 没啥区别.

一般后台返回的数据都有固定的格式,比如:

{
    "msg": "请求成功",
    "code": 1001,
    "data": {...}
}
或者:
{
    "msg": "请求成功",
    "code": 1001,
    "data": [...]
}
复制代码

将其转换成对应的模型:

public struct DataResponse<T> {
    public init (){}
    public var code: Int = -1
    public var msg: String = ""
    public var data: T?
}
public struct ListResponse<T> {
    public init (){}
    public var code: Int = -1
    public var msg: String = ""
    public var data: [T] = []
}
复制代码

我们实现一个 Protocol 继承自 HandyJSON (HandyJSON本身也是协议),然后是我们的 Response 遵守这个协议.

public protocol RequestProtocol: HandyJSON {
    static func request(api: API, params: [String: Any]?, result: ((ResponseResult<Self>)->())?)
}

public extension RequestProtocol where Self: HandyJSON {
    static func request(api: API, params: [String: Any]?, result: ((ResponseResult<Self>)->())?) {
        let completionHandle: ((Result<Data,Error>)->()) = { res in
            switch res {
            case .success(let data):
                let jsonStr = String(data: data, encoding: .utf8)
                #if DEBUG
                print("url: \(api.path)")
                print("response:")
                print(jsonStr ?? "")
                #endif
                ///不是 json 数据,抛出 json 解析失败错误
                guard let jsonObj = self.self.deserialize(from: jsonStr) else {
                    result?(.failure(.deserializeFailed))
                    return
                }
                result?(.success(jsonObj))
            case .failure(_):
                ///处理错误 抛出去
                result?(.failure(.requestFailed))
            }
        }
        if api.method == .post {
            NetworkManager.shared.post(path: api.path, params: params, result: completionHandle)
        }else{
            NetworkManager.shared.get(path: api.path, params: params, result: completionHandle)
        }
    }
}
复制代码

然后久可以如下优雅的做请求:

NetworkManager.shared.baseUrl = "https://api.douban.com"
BookResponse.request(api: .getBookDetail, params: nil) { (res) in
    guard res.isSuccess else { return }
    print(res.value?.toJSON())
}
复制代码
{"msg":"invalid_apikey","code":104,"request":"POST /v2/book/1220562"}

API 是这么样的结构体

public struct API {
    var path: String
    var method: RequestMethod
    init(path: String, method: RequestMethod = .post) {
        self.path = path
        self.method = method
    }
}
///可以通过这种方式 减少硬编码可能会带来的错误
extension API {
    static let getBookDetail = API(path: "/v2/book/1220562")
}
复制代码

Demo地址


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

剑指Offer

剑指Offer

何海涛 / 电子工业出版社 / 2014-6-1 / CNY 55.00

《剑指Offer——名企面试官精讲典型编程题(纪念版)》是为纪念本书英文版全球发行而推出的特殊版本,在原版基础上新增大量本书英文版中的精选题目,系统整理基础知识、代码质量、解题思路、优化效率和综合能力这5个面试要点。全书分为8章,主要包括面试流程:讨论面试每一环节需要注意的问题;面试需要的基础知识:从编程语言、数据结构及算法三方面总结程序员面试知识点;高质量代码:讨论影响代码质量的3个要素(规范性......一起来看看 《剑指Offer》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

MD5 加密
MD5 加密

MD5 加密工具