使用Swift開發一個貝樂虎啓蒙App - 網絡請求封裝|8月更文挑戰

前言

上一篇文章咱們已經獲取到貝樂虎啓蒙App的一些圖片資源,這一篇咱們就開始封裝一下網絡請求。在封裝以前咱們要對虎啓蒙App進行抓包,這裏咱們使用Charles`,這個相信你們都不陌生吧,若是有不瞭解的能夠看下這篇文章對於Charles的使用android

使用Charles抓包

下面是首頁的UI,git

WechatIMG159.jpeg

咱們先來抓個首頁的數據試試,打開app後請求到很多的數據,可是沒有首頁頂部的推薦、唱兒歌、看動、讀繪本、聽故事,這五個應該是本地寫死的github

截屏2021-08-03 10.15.16.png

接下來咱們分別切換推薦、唱兒歌、看動、讀繪本、聽故事來獲取數據json

截屏2021-08-03 10.25.40.png

如今首頁的數據已經獲取到了,接下來咱們就開始封裝網絡請求api

網絡請求封裝

Swift中網絡請求咱們使用 Alamofire,而後對其進一步封裝。
markdown

首先咱們使用CocoaPods導入Alamofire庫,而後新建一個NetworkManager網絡管理類,引入Alamofire網絡

NetworkManager是一個單例,裏面增長一個dataRequest字典屬性,其key是請求接口的URL參數組成的,value是一個請求任務(DataRequest),爲何要增長這個屬性呢?是由於準備用鏈式調用,方便後面取出響應數據app

import Alamofire

class NetworkManager: NSObject {

    static let share = NetworkManager()
    private var dataRequest = [String: DataRequest]()
    
    public func request(_ url: String,
                        method: HTTPMethod = .post,
                        parameters: Parameters? = nil,
                        encoding: ParameterEncoding = JSONEncoding.default,
                        headers: HTTPHeaders? = nil) -> NetworkManager {
                        
        let key = requestKey(url, parameters)
        let request = AF.request(url,
                                 method: method,
                                 parameters: parameters,
                                 encoding: encoding,
                                 headers: headers)
        dataRequest[key] = request
        return self

    }
    
    public func responseData(completion: @escaping (Data) -> Void,
                             failure: @escaping (AFError) -> Void) {
        dataRequest.forEach { key, request in
            dataRequest.removeValue(forKey: key)
            request.responseData { response in
                switch response.result {
                case let .success(data):
                    completion(data)
                case let .failure(error):
                    failure(error)
                }
            }
        }
    }
}
複製代碼

如今已經初步封裝完成,先看看能不能請求數據oop

NetworkManager.share
    .request("https://vd.ubestkid.com/api/v1/feature/qmtab_tj3.json", parameters: parameters)
    .responseData { data in
        debugPrint(data)
     } failure: { error in
}
複製代碼

請求是能夠的,請求結果這裏就不展現了。post

雖說這個網絡請求可以使用,可是請求參數和數據返回這塊還不夠友好,接下來咱們對請求參數優化下。

由於每一個請求接口都有urlmethodparametersencodingheaders等,因此仍是用協議來處理吧

網絡請求入參處理

一、新增一個TargetType協議,由於每一個url裏面有公共的部分,因此url拆成baseURLpath

public protocol TargetType {
    var baseURL: String { get }
    var path: String { get }
    var method: HTTPMethod { get }
    var parameters: [String: Any] { get }
    var encoding: ParameterEncoding { get }
    var headers: HTTPHeaders? { get }
}
複製代碼

而後給TargetType來個extension,給全部屬性一個默認值

extension TargetType {

    var baseURL: String {
        return "https://vd.ubestkid.com/"

    }
    
    var path: String {
        return ""
    }
    
    var method: HTTPMethod {
        return .post

    }
    
    var parameters: [String: Any] {

        return [:]

    }
    
    var encoding: ParameterEncoding {
        return JSONEncoding.default

    }
    
    var headers: HTTPHeaders? {
        return nil
    }
}
複製代碼

二、回到NetworkManager裏面,修改request方法入參

public func request(_ target: TargetType) -> NetworkManager {

        let url = target.baseURL + target.path
        let key = requestKey(url, target.parameters)
        let request = AF.request(url,
                                 method: target.method,
                                 parameters: target.parameters,
                                 encoding: target.encoding,
                                 headers: target.headers)

        dataRequest[key] = request
        
        return self
    }
複製代碼

TargetTypeextension裏面新增一個request()方法,在該方法裏面調用NetworkManagerrequest()方法

extension TargetType {

    func request() -> NetworkManager {
        return NetworkManager.share.request(self)
    }
}
複製代碼

三、新建一個Network,接口請求入口,之後接口都按照模塊分

struct Network {}
複製代碼

如今就以首頁的接口看下怎麼掉用吧,在Networkextension裏面新增一個枚舉Home,之後首頁的接口都放在Home裏面,Home裏面加個list,給個path參數,由於推薦、唱兒歌、看動、讀繪本、聽故事接口就是qmtab_後面不同,並且入參都是同樣的

推薦: .../api/v1/feature/qmtab_tj3.json
唱兒歌: .../api/v1/feature/qmtab_eg3.json
看動: .../api/v1/feature/qmtab_dh3.json
讀繪本: .../api/v1/feature/qmtab_hb3.json
聽故事: .../api/v1/feature/qmtab_gs3.json

extension Network {
    enum Home {
        case list(path: String)
    }
}

extension Network.Home: TargetType {

    var path: String {
        switch self {
        case let .list(path):
            return "api/v1/feature/qmtab_\(path).json"
        }
    }
    
    var parameters: [String: Any] {
        switch self {
        case .list:
            return ["mac": "",
                    "exp10": 60,
                    "exp2": 1,
                    "ua": "",
                    "devicetype": 1,
                    "srcApp": "com.ubestkid.collection",
                    "carrier": "46002",
                    "svip_status": 2,
                    "impsize": 1,
                    "exp7": 55,
                    "exp3": 73,
                    "version": "4.0",
                    "make": "apple",
                    "bannersafe": 0,
                    "oaid": "",
                    "sh": 2208,
                    "network": 1,
                    "vps": 10,
                    "sw": 1242,
                    "cpId": "blh",
                    "splashsafe": 0,
                    "channel": "c2",
                    "exp8": 55,
                    "pkg": "com.ubestkid.collection",
                    "exp4": 34,
                    "model": "iPhone8,2",
                    "osv": "14.6",
                    "idfa": "",
                    "ppi": 401,
                    "apiVersion": "1.1.0",
                    "exp9": 37,
                    "os": 1,
                    "androidid": "",
                    "exp5": 39,
                    "ak": "8f75a52eadde46239f2227ba64eab72b",
                    "exp1": 58,
                    "egvip_status": 2,
                    "age": "1",
                    "appver": "3.8.3",
                    "installtime": 1625143625930,
                    "res_type": 0,
                    "ip": "",
                    "imei": "",
                    "userId": "",
                    "exp6": 4]
        }
    }
}
複製代碼

而後掉用就以下面:

Network.Home
    .list(path: "tj3")
    .request()
    .responseData { data in
            
} failure: { error in
        
}
複製代碼

數據返回處理

如今是能夠正常請求了,可是怎麼處理返回的數據呢?這裏咱們使用蘋果地帶的Codable協議,推薦一個CleanJSON庫來轉成model,根據首頁接口返回的數據,咱們創建一個Response基類,遵照Codable

截屏2021-08-03 14.47.22.png

struct Response<T: Codable>: Codable {
    let errorCode: Int
    let errorMessage: String
    let result: T
    
    var success: Bool {
        return errorCode == 0
    }

    var appError: AppError {
        return AppError(code: errorCode, errorMessage: errorMessage)
    }
}
複製代碼

回到NetworkManager裏面修改responseData()方法

public func responseData<T: Codable>(_ type: T.Type,
                                       completion: @escaping (Response<T>) -> Void,
                                        failure: @escaping (AppError) -> Void) {
        dataRequest.forEach { key, request in
            dataRequest.removeValue(forKey: key)
            request.responseData { response in
                switch response.result {
                case let .success(data):
                    if let responseData = try? CleanJSONDecoder().decode(Response<T>.self, from: data) {
                        if !responseData.success {
                            failure(responseData.appError)
                            return
                        }
                        completion(responseData)
                    } else {
                        failure(AppError(code: 0000, errorMessage: "數據解析失敗"))
                    }
                case let .failure(error):
                    failure(error.appError)
                }
           }
      }
 }
複製代碼

如今整個網絡請求算是封裝完成了,最後開下使用吧

Network.Home
    .list(path: "tj3")
    .request()
    .responseData(RecModel.self) { model in
        debugPrint(model.result)
} failure: { error in
        
}
複製代碼
相關文章
相關標籤/搜索