项目开发中的接口比较多,在使用 moya 时会使用多个类,为避免一些代买的重复书写,做了一些封装处理,网络使用 Alamofire,数据解析使用 Moya-ObjectMapper
没有什么太多的理论,都在代码里了(方便复制)
import ObjectMapper
import Moya
///具体问题具体分析,应根据接口实际返回数据结构来定
class ResponseModel: NSObject,Mappable {
/// 返回码
var code:Int = 0
/// 信息
var message:String = ""
/// 数据
var data:Any?
override init() {super.init()}
init(_ code: Int, message:String, data:Any? = nil) {
self.code = code
self.message = message
self.data = data
}
class func success(_ data:Any) ->ResponseModel{
return ResponseModel(200, message: "SUCCESS", data: data)
}
class func faild(_ message:String? = "FAILD") ->ResponseModel{
return ResponseModel(400, message: message ?? "FAILD", data: nil)
}
required init?(map: Map) {}
//若接口返回的是msg,则应当这么写message <- map["msg"],或则直接将属性message修改为msg,然后msg <- map["msg"]
func mapping(map: Map) {
code <- map["code"]
message <- map["message"]
data <- map["data"]
}
}
import Moya
class NetWorkManager {
/// 处理成功的返回结果
static func getResponse(_ success:Moya.Response) ->ResponseModel {
var responseModel:ResponseModel = ResponseModel()
do {
responseModel = try success.mapObject(ResponseModel.self)
//这里得注意下,有时候后台返回的数据类型无法解析,就得做其他特殊处理
//之前遇到过,由于数据返回的编码方式不同,导致无法解析,我便使用了下面的方法
// let cfEnc = CFStringEncodings.GB_18030_2000//具体问题具体分析哈
// let enc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(cfEnc.rawValue))
// if let str = String(data: success.data, encoding: String.Encoding(rawValue: enc)), let obj = str.data(using: .utf8), let dic = try JSONSerialization.jsonObject(with: obj, options: .mutableContainers) as? [String: Any],let res = ResponseModel(JSON: dic) {
// responseModel = res
// }else{
// responseModel.code = 400
// responseModel.message = "无法解析网络数据"
// }
}catch{
// 对无法解析情况的处理,可以根据自己项目来定
responseModel.code = 400
responseModel.message = "无法解析网络返回数据"
}
//TODO: 根据各自业务需求,可对一些返回结果做出特殊处理
if responseModel.code == 200 ,responseModel.message == "SUCCESS",responseModel.data == nil {
responseModel.message = "没有更多了"
}
return responseModel
}
/// 处理失败的返回结果
static func getResponse(_ error:MoyaError) ->ResponseModel {
let responseModel:ResponseModel = ResponseModel()
responseModel.code = 500
responseModel.message = error.errorDescription ?? "网络访问出错"
return responseModel
}
/// 获取请求头
///
/// - Parameter token: 是否包含token
/// - Returns: <#return value description#>
static func getHeaders(_ token:Bool = true) ->[String:String] {
var result:[String:String] = ["Content-type" : "application/json"]
if token {
result["Token"] = "这里写用户的token"
}
result["其他key"] = "key对应的value"
return result
}
}
import Moya
import Alamofire
extension MoyaProvider {
static func custom(
endpointClosure: @escaping Moya.MoyaProvider<Target>.EndpointClosure = HZJMoyaTool<Target>.endpointClosure,
requestClosure: @escaping Moya.MoyaProvider<Target>.RequestClosure = HZJMoyaTool<Target>.requestResultClosure,
stubClosure: @escaping Moya.MoyaProvider<Target>.StubClosure = HZJMoyaTool<Target>.stubClosure,
callbackQueue: DispatchQueue? = nil,
session: Moya.Session = HZJMoyaTool<Target>.session(),
plugins: [Moya.PluginType] = HZJMoyaTool<Target>.authPlugins(),
trackInflights: Bool = false) -> MoyaProvider{
return MoyaProvider.init(endpointClosure: endpointClosure, requestClosure: requestClosure, stubClosure: stubClosure, callbackQueue: callbackQueue, session: session, plugins: plugins, trackInflights: trackInflights)
}
func hzj_Request(_ target: Target, callbackQueue: DispatchQueue? = .none, progress: ProgressBlock? = .none, finishBlock:@escaping ((ResponseModel)->Void)) {
self.request(target, callbackQueue: callbackQueue, progress: progress) { (result) in
switch result {
case let .success(response):
finishBlock(NetWorkManager.getResponse(response))
case let .failure(error):
finishBlock(NetWorkManager.getResponse(error))
}
}
}
}
struct HZJMoyaTool<Target: TargetType> {
static func endpointClosure(for target: Target) -> Endpoint {
let url = target.baseURL.appendingPathComponent(target.path).absoluteString
let endpoint = Endpoint(url: url, sampleResponseClosure: {.networkResponse(200,target.sampleData)}, method: target.method, task: target.task, httpHeaderFields: target.headers)
// endpoint.adding(newHTTPHeaderFields:["Content-Type" : "application/x-www-form-urlencoded","ECP-COOKIE" : ""])
return endpoint
}
static func requestResultClosure(for endpoint: Endpoint, closure: MoyaProvider<Target>.RequestResultClosure) {
do {
var urlRequest = try endpoint.urlRequest()
urlRequest.timeoutInterval = 60//设置网络超时时间
// urlRequest.cachePolicy = .returnCacheDataElseLoad
closure(.success(urlRequest))
} catch MoyaError.requestMapping(let url) {
closure(.failure(MoyaError.requestMapping(url)))
} catch MoyaError.parameterEncoding(let error) {
closure(.failure(MoyaError.parameterEncoding(error)))
} catch {
closure(.failure(MoyaError.underlying(error, nil)))
}
}
static func stubClosure(_: Target) -> Moya.StubBehavior {
// return Moya.StubBehavior.immediate//使用sampleData中返回的测试数据
return Moya.StubBehavior.never
}
static func session() -> Session {
let defaultSession = MoyaProvider<Target>.defaultAlamofireSession()
// let configuration = URLSessionConfiguration.default
// configuration.headers = defaultSession.sessionConfiguration.headers
// configuration.httpAdditionalHeaders = defaultSession.sessionConfiguration.httpAdditionalHeaders
let configuration = defaultSession.sessionConfiguration
if let path: String = Bundle.main.path(forResource: "xxx", ofType: "cer") {
///添加证书
do {
let certificationData = try Data(contentsOf: URL(fileURLWithPath: path)) as CFData
if let certificate = SecCertificateCreateWithData(nil, certificationData){
let certificates: [SecCertificate] = [certificate]
let policies: [String: ServerTrustEvaluating] = ["domain": PinnedCertificatesTrustEvaluator(certificates: certificates, acceptSelfSignedCertificates: true, performDefaultValidation: true, validateHost: true)]
let manager = ServerTrustManager(allHostsMustBeEvaluated: false, evaluators: policies)
return Session(configuration: configuration, serverTrustManager: manager)
}
} catch {
return Session(configuration: configuration)
}
}
return Session(configuration: configuration)
}
static func authPlugins() -> [Moya.PluginType] {
return []
// return [AccessTokenPlugin{_ in User.shared.token}]
}
}
import Moya
let LoginLogManager = MoyaProvider<LoginLogAPI>.custom()
enum LoginLogAPI {
///添加登录日志(type:0-web 1-app)
case addLoginLog(type:Int)
///子模块,将部分接口抽离出去
case childApi(child:ChildAPI)
}
extension LoginLogAPI:TargetType {
var baseURL: URL {
switch self {
case . childApi(child: let child):
return child.baseURL
default:
return URL(string: AppRootApi + "/api/appHtLoginLog/")!
}
}
var path: String {
switch self {
case . childApi(child: let child):
return child.path
case .addLoginLog(type: _):
return "addLoginLog"
}
}
var method: Moya.Method {
switch self {
case . childApi(child: let child):
return child.method
default:
return .post
}
}
var sampleData: Data {
switch self {
case . childApi(child: let child):
return child.sampleData
default:
return "{sampleDataKey:sampleDataValue}".data(using: String.Encoding.utf8)!
}
}
var task: Task {
var params:[String:Any] = [:]
switch self {
case . childApi(child: let child):
return child.task
case .addLoginLog(type: let type):
params["type"] = type
return .requestCompositeParameters(bodyParameters: [:], bodyEncoding: URLEncoding.httpBody, urlParameters: params)
}
}
var headers: [String : String]? {
switch self {
case . childApi(child: let child):
return child.headers
default:
return NetWorkManager.getHeaders(true)
}
}
}
当模块中接口较多时,或者部分接口需要统一特殊处理时,可以将部分接口抽出来,使用子模块
///子模块
enum ChildAPI {
case refreshInfo(name:String)
}
extension ChildAPI:TargetType {
var baseURL: URL {
return URL(string: AppRootApi + "/api/appHtLoginLog/")!
}
var path: String {
switch self {
case .refreshInfo(name: _):
return "refreshInfo"
}
}
var method: Moya.Method {
switch self {
default:
return .post
}
}
var sampleData: Data {
return "{sampleDataKey:sampleDataValue}".data(using: String.Encoding.utf8)!
}
var task: Task {
var params:[String:Any] = [:]
switch self {
case .refreshInfo(name: let name):
params["name"] = name
return .requestParameters(parameters: params, encoding: JSONEncoding.default)
}
}
var headers: [String : String]? {
switch self {
default:
return NetWorkManager.getHeaders(false)
}
}
}
func test() {
LoginLogManager.hzj_Request(.addLoginLog(type: 1)) { [weak self](responseModel) in
guard let strongSelf = self else { return }
if responseModel.code == 200 {
print("成功")
}else{
print("失败")
}
}
}
func test2() {
LoginLogManager.hzj_Request(.childApi(.refreshInfo(name: "test"))) { [weak self](responseModel) in
guard let strongSelf = self else { return }
if responseModel.code == 200 {
print("成功")
}else{
print("失败")
}
}
}
大概就这样,以上都是一些统一的处理,若要有一些特殊的接口,大家就只用自己弄了