测试阶段,还未曾放到项目中使用,后续会继续优化调整,初始版本
pod 'Moya', '14.0.0'
pod 'HandyJSON', '5.0.3-beta'
Xcode 13.2
Moya 在对于 API 的封装是基于 enum,通过对于枚举不同端点的不同用法,生成请求。
如果项目小可以只有一个API.swift, 如果项目比较大,可以分模块,分成几个API.swift
public enum HJApi {
case zen
case version([String: String])
case sendMsg(String)
case uploadHeadImage(parameters: [String:Any], imageDate:Data)
}
extension HJApi: TargetType {
///域名
public var baseURL: URL {
switch self {
case .version(_):
return URL(string: "www.abc.com")!
default:
return URL(string: "www.def.com")!
}
}
///请求地址放到这里
public var path: String {
switch self {
case .zen:
return "/abc/abcdef"
case .version(_):
return "/abc/abcdefghi"
case .sendMsg(let msg):
return "/abcd/abcdef/\(msg)"
case .uploadHeadImage(parameters: _, imageDate: _):
return "/file/user/upload.jhtml"
}
}
///接口的请求类型
public var method: Moya.Method {
switch self {
case .zen:
return .get
default:
return .post
}
}
///请求的参数在这里处理
public var task: Task {
switch self {
case .version(let parameters):
return .requestParameters(parameters: parameters, encoding: JSONEncoding.default)
case .uploadHeadImage(parameters: let parameters, imageDate: let imgDate):
let formData = MultipartFormData(provider: .data(imgDate), name: "file",
fileName: "hangge.png", mimeType: "image/png")
return .uploadCompositeMultipart([formData], urlParameters: parameters)
default:
return .requestPlain
}
}
public var validationType: ValidationType {
return .successCodes
}
// 用于单元测试
public var sampleData: Data {
return "{}".data(using: String.Encoding.utf8)!
}
public var headers: [String: String]? {
return [
"content-type": "application/json;charset=utf-8;",
"platform": "ios",
]
}
}
/// 网络请求发送的核心初始化方法,创建网络请求对象
private let provider = MoyaProvider<MultiTarget>(endpointClosure: endpointClosure, requestClosure: requestClosure, plugins: [networkPlugin], trackInflights: false)
///超时时长
private var requestTimeOut: Double = 30
/// 网络请求的基本设置,这里可以拿到是具体的哪个网络请求,可以在这里做一些设置
private let endpointClosure = {(target: TargetType) -> Endpoint in
let url = target.baseURL.absoluteString + target.path
var task = target.task
var endPoint = Endpoint(url: url,
sampleResponseClosure: { .networkResponse(200, target.sampleData) },
method: target.method,
task: task,
httpHeaderFields: target.headers)
if let apiTarget = target as? MultiTarget,
let tar = apiTarget.target as? HJApi {
switch tar {
case .zen:
requestTimeOut = 20
return endPoint
default:
return endPoint
}
}
return endPoint
}
/// 网络请求的设置
private let requestClosure = { (endpoint: Endpoint, done: MoyaProvider.RequestResultClosure) in
do {
var request = try endpoint.urlRequest()
// 设置请求时长
request.timeoutInterval = requestTimeOut
// 打印请求参数
if let requestData = request.httpBody {
print("请求的url:\(request.url!)" + "\n" + "\(request.httpMethod ?? "")" + "发送参数" + "\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
} else {
print("请求的url:\(request.url!)" + "\(String(describing: request.httpMethod))")
}
if let header = request.allHTTPHeaderFields {
print("请求头内容\(header)")
}
done(.success(request))
} catch {
done(.failure(MoyaError.underlying(error, nil)))
}
}
/// NetworkActivityPlugin插件用来监听网络请求,界面上做相应的展示
/// 但这里我没怎么用这个。。。 loading的逻辑直接放在网络处理里面了
private let networkPlugin = NetworkActivityPlugin.init { changeType, _ in
print("networkPlugin \(changeType)")
// targetType 是当前请求的基本信息
switch changeType {
case .began:
print("开始请求网络")
case .ended:
print("结束")
}
}
///返回数据
struct myResponseData: HandyJSON {
var isSuccess: Bool?
var code: String?
var message: String?
var data: String?
}
此处是返回NetWork网络请求封装
public class NetWork: NSObject {
}
在NETWork 里面的返回单个model和数组model以及字符串的网络请求封装,
///返回单 model 网络请求
public class func request<T:HandyJSON>(target: TargetType,
modelType: T.Type,
successBlock: @escaping (_ code: String, _ model: T?, _ msg:String) -> Void,
failureBlock:@escaping (_ code: String, _ msg:String) -> Void){
provider.request(MultiTarget(target)) { result in
switch result {
case let .success(response):
if let json = try? response.mapJSON() {
responseData(json, { code, result, msg in
return successBlock(code, modelType.deserialize(from: result), msg)
}, failureBlock)
} else {
return failureBlock("-1", "返回数据获取失败")
}
case let .failure(error):
print("failure\(error.localizedDescription)")
return failureBlock("-1", msgNetError)
}
}
}
返回数组 model 网络请求
///返回数组 model 网络请求
public class func request<T:HandyJSON>(target: TargetType,
modelTypes: [T].Type,
successBlock: @escaping (_ code: String, _ models: [T?], _ msg: String) -> Void,
failureBlock:@escaping (_ code: String, _ msg:String) -> Void){
provider.request(MultiTarget(target)) { result in
switch result {
case let .success(response):
if let json = try? response.mapJSON() {
responseData(json, { code, result, msg in
return successBlock(code, modelTypes.deserialize(from: result) ?? [], msg)
}, failureBlock)
} else {
return failureBlock("-1", "返回数据获取失败")
}
case let .failure(error):
print("failure\(error.localizedDescription)")
return failureBlock("-1", msgNetError)
}
}
}
返回字符串 网络请求
///返回字符串 网络请求
public class func request(target: TargetType,
successBlock: @escaping (_ code: String, _ result: String, _msg: String) -> Void,
failureBlock:@escaping (_ code: String, _ msg:String) -> Void){
provider.request(MultiTarget(target)) { result in
switch result {
case let .success(response):
if let json = try? response.mapJSON() {
responseData(json, { code, result, msg in
return successBlock(code, result, msg)
}, failureBlock)
} else {
return failureBlock("-1", "返回数据获取失败")
}
case let .failure(error):
print("failure\(error.localizedDescription)")
return failureBlock("-1", msgNetError)
}
}
}
数据解析封装
///数据处理
class func responseData(
_ data: Any,
_ successBlock: @escaping (_ code: String, _ result: String, _ msg:String) -> Void,
_ failureBlock:@escaping (_ code: String, _ msg:String) -> Void) {
if let obj = JSONDeserializer<myResponseData>.deserializeFrom(dict: data as? [String:Any]) {
let message = obj.message ?? msgNetError
guard let code = obj.code, !code.isEmpty else {
return failureBlock("-1", message)
}
if "201" == code {
return failureBlock(code, message)
}
if "200" == code {
guard let dataEncode:String = obj.data, !dataEncode.isEmpty else {
return successBlock(code, "", message)
}
DDLogInfo("\n<数据解析结果>:\n\(String(describing: result))")
successBlock(code, result ?? "", message)
}else{
return failureBlock(code, message)
}
}else{
return failureBlock("-1", msgNetError)
}
}
model
struct HJVersionUpdateModel: HandyJSON {
var versionUpgradeId: String?
var content: String?
var upgradeType: String?
var downLoadUrl: String?
var createTime: String?
var updateTime: String?
}
网络请求, 在这只需要传参就好了,请求方式,请求链接,已经在API.swift处理过了
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
let parameters = ["appType":"ios", "version": version]
NetWork.request(target: HJApi.version(parameters),
modelType: HJVersionUpdateModel.self) { code, model,msg in
} failureBlock:nil}
在post的task 中,使用了.requestParameters(parameters: par, encoding: URLEncoding.default)
问题原因:encoding问题
如果是 POST请求为 JSONEncoding.default
如果是 GET请求为 URLEncoding.default 或者 task设置 .requestPlain
解决方法借鉴自:swift 框架 moya post 请求遇到的坑
public var task: Task {
switch self {
case .version(let parameters):
return .requestParameters(parameters: parameters, encoding: JSONEncoding.default)
default:
//get请求一下两种均可,建议第二种
//return .requestParameters(parameters: [:], encoding: URLEncoding.default)
return .requestPlain
}
}
demo 传送门
demo链接https://gitee.com/hcapp/hjnet.git