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

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

内容简介:网络请求是 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地址


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

查看所有标签

猜你喜欢:

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

Parsing Techniques

Parsing Techniques

Dick Grune、Ceriel J.H. Jacobs / Springer / 2010-2-12 / USD 109.00

This second edition of Grune and Jacobs' brilliant work presents new developments and discoveries that have been made in the field. Parsing, also referred to as syntax analysis, has been and continues......一起来看看 《Parsing Techniques》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具