上一篇文章咱們已經獲取到貝樂虎啓蒙App
的一些圖片資源,這一篇咱們就開始封裝一下網絡請求。在封裝以前咱們要對虎啓蒙App進行抓包,這裏咱們使用
Charles`,這個相信你們都不陌生吧,若是有不瞭解的能夠看下這篇文章對於Charles的使用android
Charles
抓包下面是首頁的UI,git
咱們先來抓個首頁
的數據試試,打開app後請求到很多的數據,可是沒有首頁
頂部的推薦、唱兒歌、看動、讀繪本、聽故事
,這五個應該是本地寫死的github
接下來咱們分別切換推薦、唱兒歌、看動、讀繪本、聽故事
來獲取數據json
如今首頁
的數據已經獲取到了,接下來咱們就開始封裝網絡請求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
雖說這個網絡請求可以使用,可是請求參數和數據返回這塊還不夠友好,接下來咱們對請求參數優化下。
由於每一個請求接口都有
url
、method
、parameters
、encoding
、headers
等,因此仍是用協議來處理吧
TargetType
協議,由於每一個url
裏面有公共的部分,因此url
拆成baseURL
和path
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
}
複製代碼
在TargetType
的extensio
n裏面新增一個request()
方法,在該方法裏面調用NetworkManager
的request()
方法
extension TargetType {
func request() -> NetworkManager {
return NetworkManager.share.request(self)
}
}
複製代碼
Network
,接口請求入口,之後接口都按照模塊分struct Network {}
複製代碼
如今就以首頁
的接口看下怎麼掉用吧,在Network
的extension
裏面新增一個枚舉Home
,之後首頁
的接口都放在Home
裏面,Home裏面加個lis
t,給個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
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
}
複製代碼