最近要做新項(xiàng)目疲吸,原來(lái)的項(xiàng)目框架老了也該升級(jí)了座每。了解了下Moya,發(fā)現(xiàn)異常的好用摘悴。很好的把接口峭梳,接口所需參數(shù),網(wǎng)絡(luò)請(qǐng)求時(shí)的自定義設(shè)置等集中到了一起。修改和增加都非常清晰葱椭。
導(dǎo)入就不說(shuō)了捂寿,貼上Github網(wǎng)址——Moya
Moya的基本思想
我們希望一些網(wǎng)絡(luò)抽象層能夠直接充分地封裝實(shí)際調(diào)用Alamofire。這應(yīng)該是簡(jiǎn)單的孵运,常見的事情是容易的秦陋,但足夠全面,復(fù)雜的事情也很容易治笨。
如果你使用Alamofire來(lái)抽象出來(lái)URLSession驳概,為什么不用東西去抽象出URL,參數(shù)等等呢旷赖?
Moya的一些很棒的功能:
- 編譯時(shí)檢查正確的API端點(diǎn)訪問(wèn)顺又。
- 讓您定義具有相關(guān)枚舉值的不同端點(diǎn)并且清晰的使用。
- 將測(cè)試樁作為一流公民杠愧,使單元測(cè)試非常簡(jiǎn)單待榔。
使用Moya
首先創(chuàng)建一個(gè)枚舉來(lái)存放你所有的API
//API 參數(shù)列表
enum bsAPI {
case getRequest
case Login(phoneNumber: String, password: String)
case testNetwork
}
對(duì)你的枚舉(bsAPI)進(jìn)行擴(kuò)展,并遵循TargetType協(xié)議流济。這里面會(huì)把請(qǐng)求的網(wǎng)址锐锣,路徑參數(shù),基本參數(shù)绳瘟,請(qǐng)求類型等等全部定義好雕憔。
extension bsAPI: TargetType {
//網(wǎng)址
var baseURL: URL{
return URL.init(string: Using_URL)!
}
//內(nèi)容拼接
var path:String{
return ""
}
var method: Moya.Method{
switch self {
case .getRequest:
return .get
default:
return .post
}
}
var parameters: [String: Any]?{
switch self {
case .Login(let phoneNumber, let password):
let words = EncodeAction(dic: ["phoneNumber":phoneNumber, "password":password], key: FUNC_Login.md5()) /// 這是我的加密方法,自己Demo就隨便一個(gè)字典就好
return ["func":"", "words":words]
case .testNetwork:
let words: String = EncodeAction(dic: Dictionary<String, Any>(), key: FUNC_Test.md5()) /// 這是我的加密方法糖声,自己Demo就隨便一個(gè)字典就好
return ["words":words]
default:
return nil
}
}
var parameterEncoding: ParameterEncoding {
return URLEncoding.default
}
/// Provides stub data for use in testing.
var sampleData: Data {
return "".data(using: .utf8)!
}
/// The type of HTTP task to be performed.
var task: Task {
return .request
}
/// Whether or not to perform Alamofire validation. Defaults to `false`.
var validate: Bool {
return false
}
}
協(xié)議定義好了之后使用就異常簡(jiǎn)單斤彼, 定義一個(gè)類型就可以開始使用了。
let provider = MoyaProvider<bsAPI>()
provider.request(.Show) { result in
// do something with result
}
但是這是最基礎(chǔ)的用法蘸泻,MoyaProvider是提供很多屬性的琉苇,下面的寫法就為你的網(wǎng)絡(luò)請(qǐng)求添加了請(qǐng)求時(shí)狀態(tài)欄轉(zhuǎn)圈(狀態(tài)欄菊花加載)和網(wǎng)絡(luò)請(qǐng)求頁(yè)面菊花加載。這樣就不用每個(gè)請(qǐng)求的時(shí)候都帶上MBProgressHUD的使用了悦施。
public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping,
requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping,
stubClosure: @escaping StubClosure = MoyaProvider.neverStub,
manager: Manager = MoyaProvider<Target>.defaultAlamofireManager(),
plugins: [PluginType] = [],
trackInflights: Bool = false) {
self.endpointClosure = endpointClosure
self.requestClosure = requestClosure
self.stubClosure = stubClosure
self.manager = manager
self.plugins = plugins
self.trackInflights = trackInflights
}
這是MoyaProvider的默認(rèn)初始化方法并扇,我們會(huì)對(duì)requestClosure和plugins這兩個(gè)屬性作出自定義。
requestClosure的自定義
let requestTimeoutClosure = { (endpoint: Endpoint<bsAPI>, done: @escaping MoyaProvider<bsAPI>.RequestResultClosure) in
guard var request = endpoint.urlRequest else { return }
request.timeoutInterval = 10 //設(shè)置請(qǐng)求超時(shí)時(shí)間
done(.success(request))
}
plugins的自定義
let networkPlugin = NetworkActivityPlugin { (type) in
switch type {
case .began:
/// 狀態(tài)欄轉(zhuǎn)圈
UIApplication.shared.isNetworkActivityIndicatorVisible = true
case .ended:
/// 狀態(tài)欄停止轉(zhuǎn)圈
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
這里用到的是Moya的網(wǎng)絡(luò)監(jiān)控插件抡诞,NetworkActivityPlugin穷蛹,繼承自PluginType。定義了狀態(tài)欄的網(wǎng)絡(luò)加載昼汗。下面要自定義一個(gè)繼承自PluginType肴熏,網(wǎng)絡(luò)請(qǐng)求時(shí)頁(yè)面加載的插件。
class RequestLoadingPlugin: PluginType {
var HUD:MBProgressHUD = MBProgressHUD.init()
func willSend(_ request: RequestType, target: TargetType) {
print("開始請(qǐng)求")
if let keyViewController = UIApplication.shared.keyWindow?.rootViewController {
HUD.mode = MBProgressHUDMode.indeterminate
HUD.bezelView.color = UIColor.lightGray
HUD.removeFromSuperViewOnHide = true
HUD.backgroundView.style = .solidColor
HUD = MBProgressHUD.showAdded(to: keyViewController.view, animated: true)
}
}
func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
print("結(jié)束請(qǐng)求")
HUD.hide(animated: true, afterDelay: 0)
guard case Result.failure(_) = result else {
return
}
/// 請(qǐng)求失敗
let errorReason: String = (result.error?.errorDescription)!
print("請(qǐng)求失敗: \(errorReason)")
/// 沒網(wǎng) "The Internet connection appears to be offline."
/// 連接不到服務(wù)器 "Could not connect to the server."
var tip = "請(qǐng)求失敗!"
if errorReason.contains("The Internet connection appears to be offline") {
tip = "網(wǎng)絡(luò)不給力,請(qǐng)檢查您的網(wǎng)絡(luò)"
}
if errorReason.contains("Could not connect to the server") {
tip = "無(wú)法連接服務(wù)器"
}
/// 使用tip文字 進(jìn)行提示
}
}
然后初始化MoyaProvider的時(shí)候帶上自定義的插件
let bsProvider = MoyaProvider<bsAPI>(requestClosure: requestTimeoutClosure , plugins: [RequestLoadingPlugin(), networkPlugin])
大功告成G曛稀M芾簟!
然后去簡(jiǎn)單使用下
bsProvider.request(bsAPI.testNetwork) { (result) in
switch result{
case let .success(res):
print("======\(res)======")
let responseDict = JSON(res.data) ///這里是導(dǎo)入了SwiftyJSON
print(responseDict)
case let .failure(error):
print("=====\(error)=========")
}
}
我是把API的定義和Manager分開了,在bsNetworkAPI文件里寫測(cè)試地址/正式地址鸦做,接口名璧疗。這樣子要找接口,改接口一目了然馁龟。
bsNetworkAPI文件代碼大致如下
import UIKit
#if DEBUG
let Using_URL = "" //測(cè)試地址
#else
let Using_URL = "" //正式地址
#endif
/// 登陸
let FUNC_Login = "login"
/// 測(cè)試
let FUNC_Test = "test"
bsNetworkManager里的代碼就是上文里面拆開講的那部分,給大家一個(gè)合集漆魔,這樣更方便學(xué)習(xí)坷檩。我就比較喜歡全部的代碼都給我,然后我在去自己拆解來(lái)看改抡。
import UIKit
import Moya
import Result
import MBProgressHUD
import SwiftyJSON
//API 參數(shù)列表
enum bsAPI {
case getRequest
case Login(phoneNumber: String, password: String)
case testNetwork
}
let networkPlugin = NetworkActivityPlugin { (type) in
switch type {
case .began:
/// 狀態(tài)欄轉(zhuǎn)圈
UIApplication.shared.isNetworkActivityIndicatorVisible = true
case .ended:
/// 狀態(tài)欄停止轉(zhuǎn)圈
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
let requestTimeoutClosure = { (endpoint: Endpoint<bsAPI>, done: @escaping MoyaProvider<bsAPI>.RequestResultClosure) in
guard var request = endpoint.urlRequest else { return }
request.timeoutInterval = 10 //設(shè)置請(qǐng)求超時(shí)時(shí)間
done(.success(request))
}
class RequestLoadingPlugin: PluginType {
var HUD:MBProgressHUD = MBProgressHUD.init()
func willSend(_ request: RequestType, target: TargetType) {
print("開始請(qǐng)求")
if let keyViewController = UIApplication.shared.keyWindow?.rootViewController {
HUD.mode = MBProgressHUDMode.indeterminate
HUD.bezelView.color = UIColor.lightGray
HUD.removeFromSuperViewOnHide = true
HUD.backgroundView.style = .solidColor
HUD = MBProgressHUD.showAdded(to: keyViewController.view, animated: true)
}
}
func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
print("結(jié)束請(qǐng)求")
HUD.hide(animated: true, afterDelay: 0)
/// 請(qǐng)求失敗
let errorReason: String = (result.error?.errorDescription)!
print("請(qǐng)求失敗: \(errorReason)")
/// 沒網(wǎng) "The Internet connection appears to be offline."
/// 連接不到服務(wù)器 "Could not connect to the server."
var tip = "請(qǐng)求失敗!"
if errorReason.contains("The Internet connection appears to be offline") {
tip = "網(wǎng)絡(luò)不給力,請(qǐng)檢查您的網(wǎng)絡(luò)"
}
if errorReason.contains("Could not connect to the server") {
tip = "無(wú)法連接服務(wù)器"
}
/// 使用tip文字 進(jìn)行提示
}
}
let bsProvider = MoyaProvider<bsAPI>(requestClosure: requestTimeoutClosure , plugins: [RequestLoadingPlugin(), networkPlugin])
extension bsAPI: TargetType {
//網(wǎng)址
var baseURL: URL{
return URL.init(string: Using_URL)!
}
//內(nèi)容拼接
var path:String{
return FUNC_Test
}
var method: Moya.Method{
switch self {
case .getRequest:
return .get
default:
return .post
}
}
var parameters: [String: Any]?{
switch self {
case .Login(let phoneNumber, let password):
let words = EncodeAction(dic: ["phoneNumber":phoneNumber, "password":password], key: FUNC_Login.md5())
return ["func":"", "words":words]
case .testNetwork:
let words: String = EncodeAction(dic: Dictionary<String, Any>(), key: FUNC_Test.md5())
return ["words":words]
default:
return nil
}
}
var parameterEncoding: ParameterEncoding {
return URLEncoding.default
}
/// Provides stub data for use in testing.
var sampleData: Data {
return "".data(using: .utf8)!
}
/// The type of HTTP task to be performed.
var task: Task {
return .request
}
/// Whether or not to perform Alamofire validation. Defaults to `false`.
var validate: Bool {
return false
}
}
還有疑問(wèn)可以去Github看看官方教程或下載官方例子——Moya
如果有對(duì)MBProgressHUD的那部分有疑問(wèn)的矢炼,可以去iOS版 - MBProgressHUD設(shè)置背景方框?yàn)橥该?/a>