swift 使用Moya進(jìn)行網(wǎng)絡(luò)請(qǐng)求

前言

測(cè)試階段蜂林,還未曾放到項(xiàng)目中使用叔磷,后續(xù)會(huì)繼續(xù)優(yōu)化調(diào)整,初始版本

環(huán)境

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

用法

1碍遍、基本模板

Moya 在對(duì)于 API 的封裝是基于 enum定铜,通過(guò)對(duì)于枚舉不同端點(diǎn)的不同用法,生成請(qǐng)求雀久。
如果項(xiàng)目小可以只有一個(gè)API.swift宿稀, 如果項(xiàng)目比較大,可以分模塊赖捌,分成幾個(gè)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")!
        }
    }
    ///請(qǐng)求地址放到這里
    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"
        }
    }
    ///接口的請(qǐng)求類型
    public var method: Moya.Method {
        switch self {
        case .zen:
            return .get
        default:
            return .post
        }
    }
    ///請(qǐng)求的參數(shù)在這里處理
    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
    }
    // 用于單元測(cè)試
    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.網(wǎng)絡(luò)請(qǐng)求初始化

/// 網(wǎng)絡(luò)請(qǐng)求發(fā)送的核心初始化方法祝沸,創(chuàng)建網(wǎng)絡(luò)請(qǐng)求對(duì)象
private let provider = MoyaProvider<MultiTarget>(endpointClosure: endpointClosure, requestClosure: requestClosure, plugins: [networkPlugin], trackInflights: false)
///超時(shí)時(shí)長(zhǎng)
private var requestTimeOut: Double = 30
/// 網(wǎng)絡(luò)請(qǐng)求的基本設(shè)置,這里可以拿到是具體的哪個(gè)網(wǎng)絡(luò)請(qǐng)求,可以在這里做一些設(shè)置
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
}
/// 網(wǎng)絡(luò)請(qǐng)求的設(shè)置
private let requestClosure = { (endpoint: Endpoint, done: MoyaProvider.RequestResultClosure) in
    do {
        var request = try endpoint.urlRequest()
        // 設(shè)置請(qǐng)求時(shí)長(zhǎng)
        request.timeoutInterval = requestTimeOut
        // 打印請(qǐng)求參數(shù)
        if let requestData = request.httpBody {
            print("請(qǐng)求的url:\(request.url!)" + "\n" + "\(request.httpMethod ?? "")" + "發(fā)送參數(shù)" + "\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
        } else {
            print("請(qǐng)求的url:\(request.url!)" + "\(String(describing: request.httpMethod))")
        }

        if let header = request.allHTTPHeaderFields {
            print("請(qǐng)求頭內(nèi)容\(header)")
        }

        done(.success(request))
    } catch {
        done(.failure(MoyaError.underlying(error, nil)))
    }
}
/// NetworkActivityPlugin插件用來(lái)監(jiān)聽網(wǎng)絡(luò)請(qǐng)求越庇,界面上做相應(yīng)的展示
/// 但這里我沒怎么用這個(gè)罩锐。。卤唉。 loading的邏輯直接放在網(wǎng)絡(luò)處理里面了
private let networkPlugin = NetworkActivityPlugin.init { changeType, _ in
    print("networkPlugin \(changeType)")
    // targetType 是當(dāng)前請(qǐng)求的基本信息
    switch changeType {
    case .began:
        print("開始請(qǐng)求網(wǎng)絡(luò)")

    case .ended:
        print("結(jié)束")
    }
}
///返回?cái)?shù)據(jù)
struct myResponseData: HandyJSON {
    var isSuccess: Bool?
    var code: String?
    var message: String?
    var data: String?
} 

3. 網(wǎng)絡(luò)請(qǐng)求封裝

此處是返回NetWork網(wǎng)絡(luò)請(qǐng)求封裝

public class NetWork: NSObject {  
}

在NETWork 里面的返回單個(gè)model和數(shù)組model以及字符串的網(wǎng)絡(luò)請(qǐng)求封裝涩惑,

///返回單 model 網(wǎng)絡(luò)請(qǐng)求
    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", "返回?cái)?shù)據(jù)獲取失敗")
                }
            case let .failure(error):
                print("failure\(error.localizedDescription)")
                return failureBlock("-1", msgNetError)
            }
        }
    }
   

返回?cái)?shù)組 model 網(wǎng)絡(luò)請(qǐng)求

 ///返回?cái)?shù)組 model 網(wǎng)絡(luò)請(qǐng)求
    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", "返回?cái)?shù)據(jù)獲取失敗")
                }
            case let .failure(error):
                print("failure\(error.localizedDescription)")
                return failureBlock("-1", msgNetError)
            }
        }
    }

返回字符串 網(wǎng)絡(luò)請(qǐng)求

    ///返回字符串 網(wǎng)絡(luò)請(qǐng)求
    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", "返回?cái)?shù)據(jù)獲取失敗")
                }
            case let .failure(error):
                print("failure\(error.localizedDescription)")
                return failureBlock("-1", msgNetError)
            }
        }
    }

數(shù)據(jù)解析封裝

///數(shù)據(jù)處理
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<數(shù)據(jù)解析結(jié)果>:\n\(String(describing: result))")
            successBlock(code, result ?? "", message)
        }else{
            return failureBlock(code, message)
        }
    }else{
        return failureBlock("-1", msgNetError)
    }
}

3.網(wǎng)絡(luò)請(qǐng)求:舉個(gè)栗子??吧

model

struct HJVersionUpdateModel: HandyJSON { 
    var versionUpgradeId: String?  
    var content: String? 
    var upgradeType: String? 
    var downLoadUrl: String? 
    var createTime: String?  
    var updateTime: String?  
} 

網(wǎng)絡(luò)請(qǐng)求, 在這只需要傳參就好了桑驱,請(qǐng)求方式竭恬,請(qǐng)求鏈接跛蛋,已經(jīng)在API.swift處理過(guò)了

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請(qǐng)求status: 400痊硕、405
在post的task 中赊级,使用了.requestParameters(parameters: par, encoding: URLEncoding.default)
問題原因:encoding問題
如果是 POST請(qǐng)求為 JSONEncoding.default
如果是 GET請(qǐng)求為 URLEncoding.default 或者 task設(shè)置 .requestPlain
解決方法借鑒自:swift 框架 moya post 請(qǐng)求遇到的坑

public var task: Task {
    switch self {
        case .version(let parameters):
        return .requestParameters(parameters: parameters, encoding: JSONEncoding.default)  
    default:
    //get請(qǐng)求一下兩種均可,建議第二種
        //return .requestParameters(parameters: [:], encoding: URLEncoding.default)
        return .requestPlain
    }
}

此處僅做筆記岔绸,如有不足理逊,請(qǐng)各位大佬指出
demo 傳送門
demo鏈接??https://gitee.com/hcapp/hjnet.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市盒揉,隨后出現(xiàn)的幾起案子晋被,更是在濱河造成了極大的恐慌,老刑警劉巖刚盈,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羡洛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡扁掸,警方通過(guò)查閱死者的電腦和手機(jī)翘县,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谴分,“玉大人锈麸,你說(shuō)我怎么就攤上這事∥悖” “怎么了忘伞?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)沙兰。 經(jīng)常有香客問我氓奈,道長(zhǎng),這世上最難降的妖魔是什么鼎天? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任舀奶,我火速辦了婚禮,結(jié)果婚禮上斋射,老公的妹妹穿的比我還像新娘育勺。我一直安慰自己,他們只是感情好罗岖,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布涧至。 她就那樣靜靜地躺著,像睡著了一般桑包。 火紅的嫁衣襯著肌膚如雪南蓬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音赘方,去河邊找鬼烧颖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蒜焊,可吹牛的內(nèi)容都是我干的倒信。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼泳梆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了榜掌?” 一聲冷哼從身側(cè)響起优妙,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎憎账,沒想到半個(gè)月后套硼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胞皱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年邪意,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片反砌。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡雾鬼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宴树,到底是詐尸還是另有隱情策菜,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布酒贬,位于F島的核電站又憨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏锭吨。R本人自食惡果不足惜蠢莺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望零如。 院中可真熱鬧躏将,春花似錦、人聲如沸埠况。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辕翰。三九已至夺衍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喜命,已是汗流浹背沟沙。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工河劝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人矛紫。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓赎瞎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親颊咬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子务甥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容