本文應(yīng)用到的Swift特性 :枚舉,函數(shù)式編程,可選項(xiàng)
本文是接著上一篇文章寫的叛复,網(wǎng)絡(luò)是我們項(xiàng)目中一塊很重要的內(nèi)容,上篇文章中的寫法可以滿足我們簡(jiǎn)單的網(wǎng)絡(luò)求情扔仓,但是卻不夠Swift褐奥。運(yùn)用Swift里的enum、Monad翘簇、范型等Swift特性撬码,可以讓我們寫出更Swift化的代碼,這篇文章是用Swift2.3寫的版保。
在以前的代碼里呜笑,我們的網(wǎng)絡(luò)回調(diào)Handle往往是這樣寫的:typealias CompletionHandle = (result:AnyObject?, error:NSError?) -> ()
func login(account:String,pwd:String,completion: (success: Bool, errorMsg: String?) -> ()) {
let loginParams= ["account": account, "password": pwd]
post(APPURLRequest("auth/login", params: loginParams)) { (success, object) -> () in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if success {
completion(success: true, errorMsg: nil)
} else {
var errorMsg= "error"
if let object = object, let passedMessage = object["message"] as? String {
errorMsg= passedMessage
}
completion(success: true, errorMsg: errorMsg)
}
})
}
}
在代碼中往往需要判斷是否有error或者是否有result夫否,又或者兩者都有值,這種寫法在Swift里不夠nice叫胁,看起來(lái)也不美觀凰慈。我們的目標(biāo)是這樣的:
func login(params:loginParams,completion:(result:NetworkResult<NSData>) -> ()) {
NetworkManager.post("path", params: loginParams)
.response { (result) in
result
.successful({ (value) in
//登錄成功 JSON解析
//拿到數(shù)據(jù)
//進(jìn)一步處理
})
.failured({ (error, obj) in
//登錄失敗 UI處理
})
}
}
運(yùn)用enum,范型
Swift里的enum可以有關(guān)聯(lián)值驼鹅,現(xiàn)在定義這樣一個(gè)enum:
enum APIResult<T>{
case failure(NSError,AnyObject?)
case success(T)
func flatMap<U>( transform : (T) throws -> U?) rethrows -> APIResult<U>{
switch self {
case let .failure(error,obj):
return .failure(error,obj)
case let .success(value):
guard let newValue = try transform(value) else{
return .failure(error,obj)
}
return .success(newValue)
}
}
func map<U>( transform: (T) throws -> U) rethrows -> APIResult<U>{
return try flatMap {
try transform($0)
}
}
}
這個(gè)enum里包含了兩個(gè)case微谓,分別對(duì)應(yīng)我們網(wǎng)絡(luò)返回的result和error,另外谤民,在enum中還寫了APIResult的解包方法堰酿,方便我們解包。范型用在這里张足,我們可以應(yīng)對(duì)網(wǎng)絡(luò)請(qǐng)求以外的對(duì)錯(cuò)結(jié)果。接下來(lái)是網(wǎng)絡(luò)的封裝坎藐,
在Alamofire中为牍,有兩個(gè)類,一個(gè)是Request岩馍,另一個(gè)是Manager碉咆,nice的解耦思路,而且還可以用Swift漂亮的鏈?zhǔn)秸{(diào)用蛀恩。其中Request負(fù)責(zé)對(duì)請(qǐng)求處理疫铜,Manager負(fù)責(zé)方法包裝,線程和回調(diào)的管理。這是我們的Enum就派上用處了双谆,completionHandle里可以用NetworkResult做為回調(diào)參數(shù)壳咕,Request可以是這樣的:
class NetworkRequest:NSMutableURLRequest{
......//可以寫多種處理
func response(completion:(result:NetworkResult<NSData>) -> ()) {
dataTask(self, completion: completion)
}
private func dataTask(request: NSMutableURLRequest, completion:(result:NetworkResult<NSData>) -> ()) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
//處理錯(cuò)誤 error
if let data = data {
let response = response as? NSHTTPURLResponse
if response != nil && response!.statusCode == 200{
completion(result: NetworkResult.success(data))
} else {
//可以自定義錯(cuò)誤 error
completion(result: NetworkResult.failed(error!, "error"))
}
}else{
completion(result: NetworkResult.failed(error!, "error"))
}
}.resume()
}
然后再封裝一個(gè)Manager,對(duì)POST顽馋、GET谓厘、DownLoad、Upload等請(qǐng)求方式進(jìn)行封裝寸谜,Manager的可以是這樣的:
class NetworkManager {
static let instance = NetworkManager()
class var shareInstance:NetworkManager{
return instance
}
private override init() {} // 阻止init方法
func post(path: String, params: Dictionary<String, AnyObject>? = nil) -> NetworkRequest {
//返回一個(gè)處理好的POST請(qǐng)求Request實(shí)例
let request = NetworkRequest(URL: NSURL(string: path)!)
request.HTTPMethod = NetworkMethodEnum.POST.rawValue
if let params = params {
var paramString = ""
for (key, value) in params {
let escapedKey = key.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())
let escapedValue = value.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())
paramString += "\(escapedKey)=\(escapedValue)&"
}
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
}
return request
}
//還包括GET竟稳,PUT,DELETE等熊痴。他爸。。
}
接下來(lái)果善,我們就可以華麗麗的調(diào)用Swift Style的網(wǎng)絡(luò)請(qǐng)求了诊笤。但是之前還要做一件事情,如果直接調(diào)用的話岭埠,我們需要處理NetworkRequest的case盏混,這樣也不夠Swift蔚鸥,我們?cè)侔袾etworkRequest進(jìn)行改造,將其中的解包方法改成如下:
func successful(success: (value: T) -> Void) -> NetworkRequest<T> {
switch self{
case let .success(value):
success(value: value)
default:
break
}
return self
}
func failured(failure:(error: NSError,obj: AnyObject?) -> Void) -> NetworkRequest<T> {
switch self {
case let .failed(error, obj):
failed(error, obj)
default:
break
}
return self
}
接下來(lái)就可以華麗麗的調(diào)用了许赃,例如下:
func login(params:loginParams,completion:(result:NetworkResult<NSData>) -> ()) {
NetworkManager.post("path", params: loginParams)
.response { (result) in
result
.successful({ (value) in
//登錄成功 JSON解析
//拿到數(shù)據(jù)
//進(jìn)一步處理
})
.failured({ (error, obj) in
//登錄失敗 UI處理
})
}
}
參考自:
包涵卿在中國(guó)Swift開(kāi)大者大會(huì)上的演講
Alamofire