上一篇文章咱们已经获取到贝乐虎启蒙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
}
复制代码