美文网首页
Moya + Codable + 泛型 构造一套网络请求

Moya + Codable + 泛型 构造一套网络请求

作者: sasky2008 | 来源:发表于2017-11-20 18:03 被阅读233次
    背景

    目前公司大部分网络使用的是socket类型的链接, 所以几乎没有什么使用Moya + Model的强需求, 我也是半路加入项目, 就在原有基础和战友们在继续, 最近公司决定把部分功能拆分一个新的App, 我骚动的心决定构造一个网络请求的小工具方便自己使用, 以下为见解和记录.

    思路

    思路: 希望使用: 泛型 + Moya + Codable
    目标, 最终类似于如下

    import WolfNet
    
    let _ = WolfNetwork.request(type: WolfApi.wolfGet, completion: { (user: User?, msg, code) in
    print(user?.name)
    print(user?.age)
    }) { (error) in
    }
    

    WolfApi.wolfGet是Moya的请求类型
    PriceModel是WolfMapper(Codable)协议的Model

    环境配置

    Xcode9+, iOS9+, Swift4.0+(支持Codable)
    Carthage/Pods 配置Moya, Alamofire环境

    开撸

    根据我司服务器返回规则来创建, 大家可以根据情况.

    // 服务器数据类似于如下, data里面是字典/数组等 
    {
         "data" : { },
         "code" : 1000,
         "msg" : "success"
     }
    
    目标是构造一个标题说明的网络请求
    1. 创建自定义Model需要的协议, 预留备用方法func didInit()
    2. 基于Codable对 Data <-> Model 相互转换
    3. 创建外部请求的API
    4. 其他辅助类创建
    5. 请求Demo
    1. 创建自定义协议支持Codable
    import Foundation
    import Moya
    import Alamofire
     // 创建自定义model遵循WolfMapper协议即可
    public protocol WolfMapper: Codable {
        mutating func didInit()
    }
    
    2.基于Codable对 Data <-> Model 相互转换
    /// 用以转换 data -> model / model -> data
    public class WolfModel {
    
    /// data -> model
    ///
    /// - Parameter data: 请求返回的data
    /// - Returns: 返回<T: WolfMapper>的model
      public static func model<T: WolfMapper>(_ data: Data?) -> T? {
        if let data = data {
            do {
                return try JSONDecoder.init().decode(T.self, from: data)
            } catch {
                // 此问题一般表示model属性的类型不匹配, 比如 age: String?, 可能服务器的参数为Int
                debugPrint("key的类型不匹配" + error.localizedDescription)
                return nil
            }
        } else {
            debugPrint("WolfModel传入data为nil")
            return nil
        }
      }
    
    /// model -> data
    ///
    /// - Parameter model: 遵守Encodable/WolfMapper的Model
    /// - Returns: 转换成功的data
      public static func data<T: Encodable>(_ model: T?) -> Data? {
        if let model = model {
            do {
                return try JSONEncoder.init().encode(model)
            } catch {
                debugPrint("将\(model.self)转成jsondata失败 \(error.localizedDescription)")
                return nil
            }
        } else {
            debugPrint("传入\(String(describing: model))为nil")
            return nil
        }
      }
    }
    
    3. 创建外部请求的API
    /// 对外的网络请求API
    public class Wolf {
    
    /// 返回一个Model的网络请求
    ///
    /// - Parameters:
    ///   - type:       Moya.type
    ///   - progress:   进度
    ///   - completion: 完成回调
    ///   - failure:    失败回调
    /// - Returns:      Cancellable
      public class func request<T: WolfMapper, R: TargetType>(type: R, progress: ProgressBlock? = nil, completion: ((T?, String?, Int?) -> ())?, failure: ((MoyaError?) -> ())?) -> Cancellable {
    
        let provider = self.createProvider(type)
        return provider.request(type, progress: progress, completion: { (event) in
            switch event {
            case let .success(response):
                WolfTransformModel.objectFromJSON(response.data, completion)
            case let .failure(error):
                debugPrint(error.localizedDescription)
                failure?(error)
            }
        })
      }
    
    /// 返回一个[Model]的网络请求
    ///
    /// - Parameters:
    ///   - type:       Moya.type
    ///   - progress:   进度
    ///   - completion: 完成回调
    ///   - failure:    失败回调
    /// - Returns:      Cancellable
      public class func requestList<T: WolfMapper, R: TargetType>(type: R, progress: ProgressBlock? = nil, completion: (([T]?, String?, Int?) -> Void)?, failure: ((MoyaError?) -> ())?) -> Cancellable {
        let provider = self.createProvider(type)
        return provider.request(type, progress: progress, completion: { (event) in
            switch event {
            case let .success(response):
                WolfTransformModel.listFromJSON(response.data, completion)
             case let .failure(error):
                debugPrint(error.localizedDescription)
                failure?(error)
            }
        })
      }
    }
    
    4. 其他辅助类创建
    /// 拼凑一下Moya的Provider
    fileprivate class func createProvider<T: TargetType>(_ target: T) -> MoyaProvider<T> {
        
        let endpointClosure = { (target: T) -> Endpoint in
            let url = target.baseURL.appendingPathComponent(target.path).absoluteString
            let endpoint: Endpoint = Endpoint(url: url, sampleResponseClosure: { () -> EndpointSampleResponse in
                return .networkResponse(200, target.sampleData)
            }, method: target.method, task: target.task, httpHeaderFields: target.headers)
            return endpoint.adding(newHTTPHeaderFields: wolf.header)
        }
        if wolf.isDebug {
            return MoyaProvider<T>(endpointClosure: endpointClosure, manager: wolf.sessionManager, plugins: [NetworkLoggerPlugin(verbose: true, responseDataFormatter: JSONResponseDataFormatter)])
        } else {
            return MoyaProvider<T>(endpointClosure: endpointClosure, manager: wolf.sessionManager)
        }
    }
    
    
    fileprivate struct WolfTransformModel {
    
      static func objectFromJSON<T: WolfMapper>(_ Json: Data, _ response: ((T?, String?, Int?) -> Void)?) {
        let tmp: WolfBaseModel<T>? = WolfModel.model(Json)
        response?(tmp?.data, tmp?.msg, tmp?.code)
      }
    
      static func listFromJSON<T: WolfMapper>(_ Json: Data, _ response: (([T]?, String?, Int?) -> Void)?) {
        let tmp: WolfBaseModels<T>? = WolfModel.model(Json)
        response?(tmp?.data, tmp?.msg, tmp?.code)
      }
    }
    
    // Moya插件, Debug使用
    private func JSONResponseDataFormatter(_ data: Data) -> Data {
      do {
        let dataAsJSON = try JSONSerialization.jsonObject(with: data)
        let prettyData =  try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
        return prettyData
      } catch {
        return data // fallback to original data if it can't be serialized.
      }
    }
    
    public let wolf = WolfParams()
    public class WolfParams {
    // 请求头
      public var isDebug = false
      public var header = [String: String]()
      public var sessionManager = DefaultAlamofireManager.sharedManager
    }
    
    
    public class DefaultAlamofireManager: Alamofire.SessionManager {
    
      static let sharedManager: DefaultAlamofireManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = Manager.defaultHTTPHeaders
        configuration.timeoutIntervalForRequest = 15
        configuration.timeoutIntervalForResource = 60
        let manager = Manager(configuration: configuration)
        manager.startRequestsImmediately = false
        return DefaultAlamofireManager(configuration: configuration)
      }()
    }
    
    5. 请求Demo
    struct WolfBaseModel<T: WolfMapper>: WolfMapper {
    // 处理 T: WolfMapper的model
    func didInit() {
    }
    var code: Int?
    var msg: String?
    var data: T?
    }
    
    struct WolfBaseModels<T: WolfMapper>: WolfMapper {
    // import Foundation
    
    struct WolfBaseModel<T: WolfMapper>: WolfMapper {
    // 处理 T: WolfMapper的model
      func didInit() {
      }
      var code: Int?
      var msg: String?
      var data: T?
    }
    
    struct WolfBaseModels<T: WolfMapper>: WolfMapper {
    //  处理返回[Model]
      func didInit() {
      }
      var code: Int?
      var msg: String?
      var data: [T]?
    }
    

    其他调试代码, 以及更加完整demo.
    请查看GitHub

    相关文章

      网友评论

          本文标题:Moya + Codable + 泛型 构造一套网络请求

          本文链接:https://www.haomeiwen.com/subject/ceovvxtx.html