swift 使用Moya进行网络请求

前言

测试阶段,还未曾放到项目中使用,后续会继续优化调整,初始版本

环境

  pod 'Moya',         '14.0.0'
  pod 'HandyJSON',    '5.0.3-beta'
  Xcode 13.2

用法

1、基本模板

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",
        ]
    }
}

2.网络请求初始化

/// 网络请求发送的核心初始化方法,创建网络请求对象
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?
} 

3. 网络请求封装

此处是返回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)
    }
}

3.网络请求:举个栗子🌰吧

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}

遇到的问题:

1、post请求status: 400、405
在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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容