美文网首页
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