2016-01-22 82 views
5

我正在使用大量的異步網絡請求(順便說一句iOS中的任何網絡請求需要異步),我正在找到方法來更好地處理來自不支持throws的Apple dataTaskWithRequest的錯誤。處理Swift 2中異步關閉錯誤的最佳方法是什麼?

我有這樣的代碼是:

func sendRequest(someData: MyCustomClass?, completion: (response: NSData?) ->()) { 
    let request = NSURLRequest(URL: NSURL(string: "http://google.com")!) 

    if someData == nil { 
     // throw my custom error 
    } 

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { 
     data, response, error in 

     // here I want to handle Apple's error 
    } 
    task.resume() 
} 

我需要解析我可以自定義錯誤和dataTaskWithRequest處理可能出現連接錯誤。 Swift 2引入了throws,但是你不能拋棄Apple的封閉,因爲他們沒有拋出支持並且運行異步。

我看到唯一的方法來添加到我的完成塊NSError返回,但正如我所知,使用NSError是舊式的Objective-C方式。 ErrorType只能用於投擲(afaik)。

使用Apple網絡關閉時處理錯誤的最佳和最現代的方法是什麼?據我所知,沒有辦法在異步網絡功能中引發任何使用。

+0

您可以使用錯誤類型不拋出:現在

,可以按如下方式使用它。即在完成處理程序中返回它。也許你可能想看看Alamofire如何處理響應/錯誤https:// github。COM/Alamofire/Alamofire /斑點/主/源/ Result.swift – doschi

回答

13

有很多方法可以解決這個問題,但我會建議使用一個完成塊,其預計Result Enum。這可能是最「快捷」的方式。

結果enum恰好有兩個狀態,成功和錯誤,這對通常的兩個可選返回值(數據和錯誤)有很大的好處,這導致了4種可能的狀態。

enum Result<T> { 
    case Success(T) 
    case Error(String, Int) 
} 

在完成塊中使用結果枚舉完成拼圖。

let InvalidURLCode = 999 
let NoDataCode = 998 
func getFrom(urlString: String, completion:Result<NSData> -> Void) { 
    // make sure the URL is valid, if not return custom error 
    guard let url = NSURL(string: urlString) else { return completion(.Error("Invalid URL", InvalidURLCode)) } 

    let request = NSURLRequest(URL: url) 
    NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in 
     // if error returned, extract message and code then pass as Result enum 
     guard error == nil else { return completion(.Error(error!.localizedDescription, error!.code)) } 

     // if no data is returned, return custom error 
     guard let data = data else { return completion(.Error("No data returned", NoDataCode)) } 

     // return success 
     completion(.Success(data)) 
    }.resume() 
} 

因爲返回值是一個枚舉,所以應該關閉它。

getFrom("http://www.google.com") { result in 
    switch result { 
    case .Success(let data): 
     // handle successful data response here 
     let responseString = String(data:data, encoding: NSASCIIStringEncoding) 
     print("got data: \(responseString)"); 
    case .Error(let msg, let code): 
     // handle error here 
     print("Error [\(code)]: \(msg)") 
    } 
} 

另一種解決方案是通過兩個完成塊,一個用於成功,一個用於出錯。沿線的東西:

func getFrom(urlString: String, successHandler:NSData -> Void, errorHandler:(String, Int) -> Void) 
0

有一個優雅的方法,利用類似JavaScript的Promise庫或類似Scala的「Future and Promise」庫。

使用Scala的風格期貨和承諾,它可能看起來如下:

你原有的功能

func sendRequest(someData: MyCustomClass?, completion: (response: NSData?) ->())

,可以按以下所示來實現。這也說明,如何創建一個承諾,一個失敗的未來,以及如何實現/提前返還拒絕承諾:

func sendRequest(someData: MyCustomClass) -> Future<NSData> { 
    guard let url = ... else { 
    return Future.failure(MySessionError.InvalidURL) // bail out early with a completed future 
    } 
    let request = ... // setup request 
    let promise = Promise<NSData>() 
    NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in 
    guard let error = error else { 
     promise.reject(error) // Client error 
    } 
    // The following assertions should be true, unless error != nil 
    assert(data != nil) 
    assert(response != nil) 

    // We expect HTTP protocol: 
    guard let response = response! as NSHTTPURLResponse else { 
     promise.reject(MySessionError.ProtocolError) // signal that we expected HTTP. 
    } 

    // Check status code: 
    guard myValidStatusCodeArray.contains(response.statusCode) else { 
     let message: String? = ... // convert the response data to a string, if any and if possible 
     promise.reject(MySessionError.InvalidStatusCode(statusCode: response.statusCode, message: message ?? "")) 
    } 

    // Check MIME type if given: 
    if let mimeType = response.MIMEType { 
     guard myValidMIMETypesArray.contains(mimeType) else { 
     promise.reject(MySessionError.MIMETypeNotAccepted(mimeType: mimeType)) 
     } 
    } else { 
     // If we require a MIMEType - reject the promise. 
    } 
    // transform data to some other object if desired, can be done in a later, too. 

    promise.fulfill(data!) 
    }.resume() 

    return promise.future! 
} 

你可能會想到一個JSON作爲迴應 - 如果請求成功。

sendRequest(myObject).map { data in 
    return try NSJSONSerialization.dataWithJSONObject(data, options: []) 
} 
.map { object in 
    // the object returned from the step above, unless it failed. 
    // Now, "process" the object: 
    ... 
    // You may throw an error if something goes wrong: 
    if failed { 
     throw MyError.Failed 
    } 
} 
.onFailure { error in 
    // We reach here IFF an error occurred in any of the 
    // previous tasks. 
    // error is of type ErrorType. 
    print("Error: \(error)") 
} 
相關問題