背景
目前公司大部分网络使用的是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"
}
目标是构造一个标题说明的网络请求
- 创建自定义Model需要的协议, 预留备用方法func didInit()
- 基于Codable对 Data <-> Model 相互转换
- 创建外部请求的API
- 其他辅助类创建
- 请求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
网友评论