2016-01-22 27 views
5

私は、非同期ネットワーク要求の多くを使用しています(ところでiOSの中の任意のネットワーク要求は非同期でする必要があります)と私はより良い、AppleのdataTaskWithRequestthrowsをサポートしていない、そこからエラーを処理する方法を見つけることです。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)でのみ使用できます。

アップルネットワーククロージャを使用しているときにエラーを処理するための最良かつ最も近代的な方法は何ですか?私が理解しているように、非同期のネットワーク機能を使用する方法はありません。

+0

あなたはスローせずにERRORTYPEを使用することができます。以下のように

さて、あなたはそれを使用することができます。完了ハンドラに戻します。おそらく、Alamofireがどのようにレスポンス/エラーhttps:// githubを扱うのかを見てみたいかもしれません。com/Alamofire/Alamofire/blob/master/Source/Result.swift – doschi

答えて

13

は、あなたがこの問題を解決することができます多くの方法がありますが、私は期待しResult Enum完了ブロックを使用することをお勧めします。これはおそらく最もスウィフトな方法でしょう。

結果の列挙型は、4つの可能な状態につながる通常の2つのオプションの戻り値(データおよびエラー)に大きな利点を正確に二つの状態、成功とエラーがあります。

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)") 
    } 
} 

もう1つの解決方法は、成功とエラーの2つの完了ブロックを渡すことです。

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)") 
} 
関連する問題