iOS swift moya的使用,二次封裝罕扎,添加loading以及數(shù)據(jù)緩存

Moya

使用 Moya 作為項(xiàng)目中的網(wǎng)絡(luò)層有段時(shí)間了,一般在Swift項(xiàng)目中丐重,我們用Alamofire來做網(wǎng)絡(luò)庫(kù).而Moya在Alamofire的基礎(chǔ)上又封裝了一層,方便我們的使用腔召。但是在實(shí)際的項(xiàng)目中我們又會(huì)對(duì)Moya進(jìn)行封裝方便項(xiàng)目的使用,下面具體說說實(shí)際項(xiàng)目中Moya是怎么使用的扮惦。
先附上項(xiàng)目地址: 項(xiàng)目地址

Moya 優(yōu)勢(shì)
1. 編譯時(shí)檢查正確的API端點(diǎn)訪問.
2. 使你定義不同端點(diǎn)枚舉值對(duì)應(yīng)相應(yīng)的用途更加明晰.
3. 提高測(cè)試地位從而使單元測(cè)試更加容易.
本文主要介紹以下內(nèi)容
1. Moya的用法
2. 設(shè)置請(qǐng)求頭部信息
3. 設(shè)置超時(shí)時(shí)間
4. 自定義插件
5. 統(tǒng)一添加 loading
6. 網(wǎng)絡(luò)層緩存
7. 打印請(qǐng)求參數(shù)及其返回?cái)?shù)據(jù)
8. 自簽名證書
一. Moya的具體用法以及代碼實(shí)現(xiàn)
// 首頁(yè)接口
let HomeProvider = MoyaProvider<HomeGoodsAPI>()


enum HomeGoodsAPI {
    case homeGoodsList // 首頁(yè)上面列表
    case homePageBelowConten // 首頁(yè)下面列表
    case goodDetail(goodId: String) // 商品詳情
    case goodCategory //商品類別信息
    case categoryGoodsList(categoryId: String,CategorySubId: String,page: Int) //商品列表

}


// 遵循 TargetType 代理 實(shí)現(xiàn)方法
extension HomeGoodsAPI: TargetType {
    //服務(wù)器地址
    public var baseURL: URL {
         return URL(string: "https://wxmini.baixingliangfan.cn/baixing/")!
    }

    var path: String {
        switch self {
            case .homeGoodsList: return "wxmini/homePageContent"
            case .homePageBelowConten: return "wxmini/homePageBelowConten"
            case .goodDetail: return "wxmini/getGoodDetailById"
            case .goodCategory: return "wxmini/getCategory"
            case .categoryGoodsList: return "wxmini/getMallGoods"

        }
    }

    var method: Moya.Method {
        return .post
    }
    
    var task: Task {
        var parmeters:[String:Any] = [:]
        switch self {
        case .homeGoodsList:
            parmeters = ["lon":"116.47118377685547","lat":"39.91233444213867"]
        case .homePageBelowConten:
            parmeters = ["page":"1"]
        case .goodDetail(let goodId):
            parmeters = ["goodId":goodId]
        case .goodCategory:
            parmeters = ["":""]
        case .categoryGoodsList(let categoryId,let CategorySubId,let page):
            parmeters = ["categoryId":categoryId,"categorySubId":CategorySubId,"page":page]
        }
        
        return .requestParameters(parameters: parmeters, encoding: URLEncoding.default)
    }
    
    var sampleData: Data {
        
        return "".data(using: String.Encoding.utf8)!
        
    }
    var headers: [String : String]? { return nil }

}

1. 初始化 HomeProvider 對(duì)象臀蛛,將創(chuàng)建的枚舉 HomeGoodsAPI 傳入。
2. 在HomeGoodsAPI 中設(shè)置接口名崖蜜,需要傳入?yún)?shù)的在枚舉中設(shè)置參數(shù)值浊仆。
3. 遵循 TargetType 代理 實(shí)現(xiàn)方法,設(shè)置 baseURL 即項(xiàng)目主域名豫领。
4. 設(shè)置path 即 接口的方法名抡柿。
5. 設(shè)置請(qǐng)求參數(shù) Task 。

在VC中調(diào)用 result 即 網(wǎng)絡(luò)返回
        HomeProvider.request(.goodCategory) { (result) in
            switch result {
            case .success(_):
            
                break
            case .failure(_):

                break
            @unknown default:

                break
            }
        }
在OC中大家習(xí)慣的將項(xiàng)目中的網(wǎng)絡(luò)請(qǐng)求統(tǒng)一封裝一個(gè)公共類等恐,在里面設(shè)置超時(shí)時(shí)間洲劣,設(shè)置請(qǐng)求頭,設(shè)置加載控件课蔬,設(shè)置網(wǎng)絡(luò)緩存囱稽,設(shè)置返回到VC中的數(shù)據(jù)格式,JSON或者data 等等二跋,新建一個(gè)NetworkManager 類战惊,用來專門處理網(wǎng)絡(luò)請(qǐng)求。
二.設(shè)置 請(qǐng)求頭 公共參數(shù)
// 網(wǎng)絡(luò)請(qǐng)求的基本設(shè)置,這里可以拿到是具體的哪個(gè)網(wǎng)絡(luò)請(qǐng)求扎即,可以在這里做一些設(shè)置
private let myEndpointClosure = { (target: API) -> Endpoint in
    /// 這里把endpoint重新構(gòu)造一遍主要為了解決網(wǎng)絡(luò)請(qǐng)求地址里面含有? 時(shí)無法解析的bug
    let url = target.baseURL.absoluteString + target.path
    var task = target.task
    /*
     如果需要在每個(gè)請(qǐng)求中都添加類似token參數(shù)的參數(shù)請(qǐng)取消注釋下面代碼
     */
//    let additionalParameters = ["token":"888888"]
//    let defaultEncoding = URLEncoding.default
//    switch target.task {
//        ///在你需要添加的請(qǐng)求方式中做修改就行吞获,不用的case 可以刪掉。谚鄙。
//    case .requestPlain:
//        task = .requestParameters(parameters: additionalParameters, encoding: defaultEncoding)
//    case .requestParameters(var parameters, let encoding):
//        additionalParameters.forEach { parameters[$0.key] = $0.value }
//        task = .requestParameters(parameters: parameters, encoding: encoding)
//    default:
//        break
//    }
    /*
     如果需要在每個(gè)請(qǐng)求中都添加類似token參數(shù)的參數(shù)請(qǐng)取消注釋上面代碼
     */

    var endpoint = Endpoint(
        url: url,
        sampleResponseClosure: { .networkResponse(200, target.sampleData) },
        method: target.method,
        task: task,
        httpHeaderFields: target.headers
    )
    requestTimeOut = 30 // 每次請(qǐng)求都會(huì)調(diào)用endpointClosure 到這里設(shè)置超時(shí)時(shí)長(zhǎng) 也可單獨(dú)每個(gè)接口設(shè)置
    switch target {
    case .homeGoodsList:
        return endpoint
    case .homePageBelowConten:
        requestTimeOut = 5
        return endpoint
    default:
        return endpoint
    }
}
三. 設(shè)置請(qǐng)求時(shí)長(zhǎng)衫哥,打印請(qǐng)求參數(shù)和數(shù)據(jù)返回
/// 網(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("\(request.url!)" + "\n" + "\(request.httpMethod ?? "")" + "發(fā)送參數(shù)" + "\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
            parmeterStr = String(data: request.httpBody!, encoding: String.Encoding.utf8)!
            
        // [URL absoluteString];
            
        } else {
            print("\(request.url!)" + "\(String(describing: request.httpMethod))")
        }
        done(.success(request))
    } catch {
        done(.failure(MoyaError.underlying(error, nil)))
    }
}
四.設(shè)置加載loading 使用 NetworkActivityPlugin 插件
// NetworkActivityPlugin插件用來監(jiān)聽網(wǎng)絡(luò)請(qǐng)求,界面上做相應(yīng)的展示
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ò)")
        
        SVProgressHUD .setDefaultMaskType(SVProgressHUDMaskType.clear)
        SVProgressHUD .setBackgroundLayerColor(UIColor .blue)
        SVProgressHUD .setDefaultStyle(SVProgressHUDStyle.light)
        SVProgressHUD .setForegroundColor(MainColor)
        SVProgressHUD .setDefaultAnimationType(SVProgressHUDAnimationType.flat)
        SVProgressHUD .show(withStatus: "加載中")
        SVProgressHUD .setMinimumDismissTimeInterval(20.0)
        
    case .ended:
        print("結(jié)束")
        SVProgressHUD .dismiss()
    }
}
五. 主網(wǎng)絡(luò)請(qǐng)求方法封裝以及數(shù)據(jù)緩存
// target HomeProvider 對(duì)象
/// isCarch 是否需要緩存襟锐,默認(rèn)false
/// carchID 緩存參數(shù)
func NetWorkRequest(_ target: API, isCarch: Bool = false, carchID: NSString = "", completion: @escaping successCallback, failed: failedCallback?, errorResult: errorCallback?) -> Cancellable? {
    
    // 先判斷網(wǎng)絡(luò)是否有鏈接 沒有的話直接返回--代碼略
    /*
     if !UIDevice.isNetworkConnect {
         print("提示用戶網(wǎng)絡(luò)似乎出現(xiàn)了問題")
         return nil
     }
     */
    
    /// 緩存代碼 設(shè)置緩存路徑
    let pathcaches = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
    let cachesDir = pathcaches[0]
    
    let mutableSting = target.baseURL.absoluteString + target.path + (carchID as String)
    let lastStr = mutableSting.replacingOccurrences(of: "/", with: "-")
    let disPath = cachesDir + "/" + lastStr + "-.text"
    if isCarch == true {
        DispatchQueue.global().async {
            do {
                /// 獲取json字符串
                let str = try String .init(contentsOfFile: disPath, encoding: String.Encoding.utf8)
                DispatchQueue.main.async {
                    /// 字符串轉(zhuǎn)化為data
                    let data = str .data(using: String.Encoding.utf8, allowLossyConversion: true)
                    completion(data! as NSData)
                }
            } catch {
                print(error)
            }
        }
    }
    
    return Provider.request(target) { result in
        // 隱藏hud
        switch result {
        case let .success(response):
            do {
                let jsonData = try JSON(data: response.data)
                print(jsonData)
                
                if isCarch == true {
                    // 緩存
                    let jsonStr = String(data: response.data, encoding: String.Encoding.utf8) ?? ""
                    DispatchQueue.global().async {
                        do {
                            try jsonStr .write(toFile: disPath, atomically: true, encoding: String.Encoding.utf8)
                        } catch {
                            print(error)
                        }
                    }
                }
                /// 這里的completion和failed判斷條件依據(jù)不同項(xiàng)目來做撤逢,為演示demo我把判斷條件注釋了,直接返回completion。

                completion(response.data as NSData)

                print("flag不為1000 HUD顯示后臺(tái)返回message" + "\(jsonData[RESULT_MESSAGE].stringValue)")

                if jsonData[RESULT_CODE].stringValue == "1000"{
                    completion(response.data as NSData)
                }else{
//                    failed?(String(data: try! response.mapJSON() as! Data, encoding: String.Encoding.utf8)!)
                }

            } catch {}
        case let .failure(error):
            // 網(wǎng)絡(luò)連接失敗蚊荣,提示用戶
            print("網(wǎng)絡(luò)連接失敗\(error)")
            errorResult?()
        }
    }
}
調(diào)用
NetWorkRequest(.homePageBelowConten(parameters: ["page":self.page]),isCarch: true,carchID: "page-\(page)" as NSString, completion: { (responseString) -> (Void) in
         // 輪播圖數(shù)據(jù)
        let json = JSON(responseString
        // 數(shù)據(jù)處理刷新UI
        }, failed: { (failedResutl) -> (Void) in
            print("服務(wù)器返回code不為0000啦~\(failedResutl)")
        }, errorResult: { () -> (Void) in
            print("網(wǎng)絡(luò)異常")
        })

首先說一下這個(gè)函數(shù)的幾個(gè)參數(shù):
1. _target HomeProvider 對(duì)象初狰,就是將Provider傳入。
2.isCarch 是否需要緩存互例,默認(rèn)false,因?yàn)橐粋€(gè)App一般情況下并不需要全部頁(yè)面緩存奢入,只是首頁(yè)或者個(gè)別頁(yè)面需要緩存來提高用戶體驗(yàn),當(dāng)不需要緩存時(shí)這個(gè)參數(shù)不用處理媳叨,需要時(shí)傳true就ok
carchID 緩存參數(shù) 這個(gè)參數(shù)主要是為了區(qū)分相同的接口不同的參數(shù)去做緩存時(shí)存在不同的路徑下面腥光,從上面的方法可以看到緩存具體的思路就是 以 target.baseURL.absoluteString + target.path 為主路徑將數(shù)據(jù)緩存到沙盒,當(dāng)網(wǎng)絡(luò)請(qǐng)求時(shí)如果沙盒中有數(shù)據(jù)開辟線程先將沙盒中的緩存取出來糊秆,回調(diào)到VC武福,展示UI。等網(wǎng)絡(luò)請(qǐng)求結(jié)束再將網(wǎng)絡(luò)端的數(shù)據(jù)回調(diào)痘番,這樣的思路捉片。那么在緩存時(shí)就要主要,同樣的接口如果參數(shù)不一樣時(shí)那么就需要傳這個(gè)carchID 了汞舱。比如此項(xiàng)目中伍纫,請(qǐng)求商品詳情數(shù)據(jù),每個(gè)商品的goodId是不一樣的昂芜,如果有需求說緩存一下商品詳情莹规,那個(gè)肯定是一個(gè)商品一個(gè)緩存路徑對(duì)應(yīng)著一個(gè)緩存文件,所以必須穿一個(gè)carchID 泌神。
3.回調(diào):我直接回掉的response.data访惜,當(dāng)然也可以在網(wǎng)絡(luò)層將數(shù)據(jù)轉(zhuǎn)化成model回調(diào)給VC這個(gè)可以隨意修改了

寫到這里基本上就可以滿足項(xiàng)目的需求了,如有遺漏腻扇,還請(qǐng)指教债热。

代碼地址

關(guān)于swift的更多知識(shí)

請(qǐng)點(diǎn)擊 swift文集!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市幼苛,隨后出現(xiàn)的幾起案子窒篱,更是在濱河造成了極大的恐慌,老刑警劉巖舶沿,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件墙杯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡括荡,警方通過查閱死者的電腦和手機(jī)高镐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來畸冲,“玉大人嫉髓,你說我怎么就攤上這事观腊。” “怎么了算行?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵梧油,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我州邢,道長(zhǎng)儡陨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任量淌,我火速辦了婚禮骗村,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呀枢。我一直安慰自己胚股,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布硫狞。 她就那樣靜靜地躺著晃痴,像睡著了一般残吩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上倘核,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天泣侮,我揣著相機(jī)與錄音紧唱,去河邊找鬼轻庆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛余爆,可吹牛的內(nèi)容都是我干的纷宇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛾方,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼像捶!你這毒婦竟也來了上陕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤作岖,失蹤者是張志新(化名)和其女友劉穎唆垃,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痘儡,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辕万,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沉删。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渐尿。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖矾瑰,靈堂內(nèi)的尸體忽然破棺而出砖茸,到底是詐尸還是另有隱情,我是刑警寧澤殴穴,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布凉夯,位于F島的核電站,受9級(jí)特大地震影響采幌,放射性物質(zhì)發(fā)生泄漏劲够。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一休傍、第九天 我趴在偏房一處隱蔽的房頂上張望征绎。 院中可真熱鬧,春花似錦磨取、人聲如沸人柿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凫岖。三九已至,卻和暖如春逢净,著一層夾襖步出監(jiān)牢的瞬間隘截,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工汹胃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留婶芭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓着饥,卻偏偏與公主長(zhǎng)得像犀农,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宰掉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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