學(xué)習(xí)目的
我只是出于對(duì)于這個(gè)框架的好奇,曾經(jīng)我在學(xué)習(xí)swift的時(shí)候,就是用的Alamofire,突然發(fā)現(xiàn)有一個(gè)很優(yōu)雅的第三方是基于這個(gè)向上封裝。并且,我很久沒(méi)有寫(xiě)swift了。
功能
本文包含了Moya的基本使用和圖片批量上傳(視頻等其它文件上傳跟圖片上傳類似)。并且應(yīng)用泛型實(shí)現(xiàn)了對(duì)返回結(jié)果統(tǒng)一做出處理,便于統(tǒng)一處理一些業(yè)務(wù)或者錯(cuò)誤。
-
實(shí)現(xiàn)協(xié)議
LoginAPIManager文件包含一個(gè)登陸的請(qǐng)求,一個(gè)批量上傳圖片的請(qǐng)求(單張請(qǐng)自行做修改)。分別傳入了對(duì)應(yīng)的參數(shù),小伙伴可以根據(jù)自己需求去寫(xiě)這個(gè)枚舉。
這里啰嗦一下,請(qǐng)求的manager請(qǐng)做好分類,這樣找起來(lái)會(huì)方便很多,根據(jù)功能,或者根據(jù)模塊等都可以。
public enum LoginAPIManager {
case login(name:String,password:String) //登錄
case uploadPictures(paramsDic:NSMutableDictionary,dataAry:NSArray) //上傳圖片
}
實(shí)現(xiàn)TargetType協(xié)議內(nèi)容,代碼中注釋很清楚了哈。
extension LoginAPIManager: TargetType {
public var baseURL: URL {
return URL(string: kBaseUrl)! //這里是aip的base
}
public var path: String {
switch self {
case .login(_, _):
return "登陸請(qǐng)求api" //(這里請(qǐng)寫(xiě)上自己的api哦)
case .uploadPictures(_, _):
return "上傳圖片api" //(這里請(qǐng)寫(xiě)上自己的api哦)
}
}
//請(qǐng)求方式
public var method: Moya.Method {
return .post
}
//請(qǐng)求任務(wù)事件,帶上參數(shù)
public var task: Task {
switch self {
case .login( let name, let password):
return .requestParameters(parameters: ["userAccount": name,"initialPassword" : password], encoding: JSONEncoding.default)
case .uploadPictures( let paramsDic, let dataAry):
let formDataAry:NSMutableArray = NSMutableArray()
for (index,image) in dataAry.enumerated() {
//圖片轉(zhuǎn)成Data
let data:Data = UIImageJPEGRepresentation(image as! UIImage, 0.9)!
//根據(jù)當(dāng)前時(shí)間設(shè)置圖片上傳時(shí)候的名字
let date:Date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd-HH:mm:ss"
var dateStr:String = formatter.string(from: date as Date)
//別忘記這里給名字加上圖片的后綴哦
dateStr = dateStr.appendingFormat("-%i.png", index)
let formData = MultipartFormData(provider: .data(data), name: "file", fileName: dateStr, mimeType: "image/jpeg")
formDataAry.add(formData)
}
return .uploadCompositeMultipart(formDataAry as! [MultipartFormData], urlParameters: paramsDic as! [String : Any])
// default:
// return .requestPlain // 沒(méi)有參數(shù)
}
}
//是否執(zhí)行Alamofire驗(yàn)證
// public var validate: Bool {
// return false
// }
//驗(yàn)證方式
public var validationType: ValidationType {
return .none
}
//這個(gè)就是做單元測(cè)試模擬的數(shù)據(jù),只會(huì)在單元測(cè)試文件中有作用
public var sampleData: Data {
return "{}".data(using: String.Encoding.utf8)!
}
//請(qǐng)求頭
public var headers: [String: String]? {
return nil
}
}
-
泛型實(shí)現(xiàn)返回結(jié)果統(tǒng)一處理
這里的endpointMapping是為了便于出錯(cuò)的情況下,清楚的知道這個(gè)請(qǐng)求的相關(guān)信息。
EDBaseAdapter這個(gè)文件,是用來(lái)對(duì)于請(qǐng)求的返回值,做統(tǒng)一處理的。在這個(gè)文件的
上層做一些緊密業(yè)務(wù)的處理,這里可以拋出問(wèn)題和做一些大家共同需要處理的業(yè)務(wù)或者錯(cuò)誤。
例如:這里可以做,如果你的項(xiàng)目session過(guò)期了,要統(tǒng)一彈出登錄頁(yè)面。還有業(yè)務(wù)出錯(cuò),錯(cuò)誤碼的解析,都可以放在這個(gè)文件里面。
這里實(shí)現(xiàn)思路是用泛型的概念,傳了枚舉作為參數(shù)的方法,生成一個(gè)實(shí)現(xiàn)了TargetType協(xié)議的類,從而去調(diào)用request方法。
這里的泛型我理解了很久,因?yàn)槲也恢罏槭裁次覀髁艘粋€(gè)LoginAPIManager里面的枚舉對(duì)象,就可以生成一個(gè)LoginAPIManager的Provider。后來(lái)我這樣理解,雖然我傳入的是一個(gè)LoginAPIManager的枚舉,但是實(shí)際上在生成MoyaProvider,用到的是LoginAPIManager實(shí)現(xiàn)的TargetType協(xié)議,枚舉泛指著LoginAPIManager這個(gè)類。個(gè)人理解,可能會(huì)不對(duì)哈。
private func endpointMapping<Target: TargetType>(target: Target) -> Endpoint {
print("請(qǐng)求連接:\(target.baseURL)\(target.path) \n方法:\(target.method)\n參數(shù):\(String(describing: target.task)) ")
return MoyaProvider.defaultEndpointMapping(for: target)
}
class EDBaseAdapter:NSObject {
class func request<T:TargetType>(
_ target:T,success successCallback: @escaping (Any) -> Void,
error errorCallback: @escaping (Int,String ) -> Void,
failure failureCallback: @escaping (MoyaError) -> Void
) {
let provider = MoyaProvider<T>(endpointClosure: endpointMapping,plugins:[])
provider.request(target) { (result) in
switch result {
case let .success(response):
do {
let data = try response.mapJSON()
let statusCode = response.statusCode
//在我的項(xiàng)目里,200和201是請(qǐng)求成功。
if (statusCode == 201 || statusCode == 200) {
//解析登錄數(shù)據(jù)
print("請(qǐng)求成功")
successCallback(data)
} else {
//請(qǐng)求報(bào)錯(cuò)提示,一般處理業(yè)務(wù)提示
print("請(qǐng)求出錯(cuò)了")
let dict = data
errorCallback(statusCode,(dict as AnyObject).object(forKey:"message") as! String)
}
} catch {
//可不做處理
}
break
case let .failure(error):
print(error)
failureCallback(error)
break
}
}
}
}
請(qǐng)求調(diào)用
下面的方法,我自己寫(xiě)完測(cè)試,是可以獲取到數(shù)據(jù)的。
//登錄調(diào)用
EDBaseAdapter.request(LoginAPIManager.login(name: self.userNameTextField.text!, password: self.passwordTextField.text!), success: { (data) in
print(data)
}, error: { (statutCode, message) in
print(statutCode,message)
}) { (error) in
print(error)
}
//上傳圖片調(diào)用
let paramsDic:NSMutableDictionary = NSMutableDictionary()
paramsDic.setValue(0, forKey: "projectType")
EDBaseAdapter.request(LoginAPIManager.uploadPictures(paramsDic: paramsDic, dataAry:NSArray.init(object: UIImage.init(named: "example")!)), success: { (data) in
print(data)
}, error: { (statutCode, message) in
print(statutCode,message)
}) { (error) in
print(error)
}
結(jié)尾
如果有什么問(wèn)題或者疑問(wèn),請(qǐng)告知我??赡芪一貜?fù)慢,或者不及時(shí)請(qǐng)諒解一下呢。但是態(tài)度絕對(duì)是很端正的。還有,妹子還是寫(xiě)OC的,swift邊寫(xiě)邊學(xué),不懂的再找資料。所以,描述或者用詞不準(zhǔn)確,請(qǐng)諒解同時(shí)請(qǐng)告知我,我看到會(huì)及時(shí)更正。希望能夠一起學(xué)習(xí)和一起進(jìn)步。
有一個(gè)簡(jiǎn)單的工程,里面包含了這些文件。是我隨手寫(xiě)的一個(gè)登陸的界面,登錄事件里面調(diào)用了文中登錄和上傳圖片的方法。
鏈接:https://pan.baidu.com/s/132XqsEzdEV0q0A2SiIdbJQ 密碼:0m51