web-dev-qa-db-ja.com

Swift 2での同期URLリクエスト

このコードは here にあり、Swift 2。

_  func send(url: String, f: (String)-> ()) {
    var request = NSURLRequest(URL: NSURL(string: url)!)
    var response: NSURLResponse?
    var error: NSErrorPointer = nil
    var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error)
    var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
    f(reply)
  }
_

しかし、関数NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error)は非推奨であり、Swiftで同期リクエストを実行する方法がわかりません。代替は非同期です。どうやらAppleは、それを同期的に実行できる唯一の関数を非推奨にしました。

どうやってやるの?

17
SpaceDog

廃止の背後には理由があります-それはまったく使用できません。疫病としての同期ネットワーク要求を避ける必要があります。これには2つの主な問題と1つの利点しかありません(使いやすいですが、非同期ではありませんか?):

  • 別のスレッドから呼び出されない場合、リクエストはUIをブロックしますが、そうする場合、非同期ハンドラーをすぐに使用しないのはなぜですか?
  • それ自体でエラーが発生した場合を除き、そのリクエストをキャンセルする方法はありません

これの代わりに、非同期リクエストを使用してください:

NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in

    // Handle incoming data like you would in synchronous request
    var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
    f(reply)
})

iOS9非推奨

IOS9では、このメソッドは廃止されているため、代わりにNSURLSessionを使用することをお勧めします。

let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in

    // Handle incoming data like you would in synchronous request
    var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
    f(reply)
}
10
Jiri Trecak

really同期的に実行したい場合は、常にセマフォを使用できます。

func send(url: String, f: (String) -> Void) {
    var request = NSURLRequest(URL: NSURL(string: url)!)
    var error: NSErrorPointer = nil
    var data: NSData

    var semaphore = dispatch_semaphore_create(0)

    try! NSURLSession.sharedSession().dataTaskWithRequest(request) { (responseData, _, _) -> Void in
        data = responseData! //treat optionals properly
        dispatch_semaphore_signal(semaphore)
    }.resume()

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

    var reply = NSString(data: data, encoding: NSUTF8StringEncoding)
    f(reply)
}

編集:ハックを追加!したがって、コードは機能しますが、実稼働コードではこれを行わないでください

Swift 3.0+(3.0、3.1、3.2、4.0)

func send(url: String, f: (String) -> Void) {
    guard let url = URL(string: url) else {
        print("Error! Invalid URL!") //Do something else
        return
    }

    let request = URLRequest(url: url)
    let semaphore = DispatchSemaphore(value: 0)

    var data: Data? = nil

    URLSession.shared.dataTask(with: request) { (responseData, _, _) -> Void in
        data = responseData
        semaphore.signal()
    }.resume()

    semaphore.wait(timeout: .distantFuture)

    let reply = data.flatMap { String(data: $0, encoding: .utf8) } ?? ""
    f(reply)
}
39
fpg1503

@ fpg1503の回答に基づいて、Swift 3:

extension URLSession {

    func synchronousDataTask(with request: URLRequest) throws -> (data: Data?, response: HTTPURLResponse?) {

        let semaphore = DispatchSemaphore(value: 0)

        var responseData: Data?
        var theResponse: URLResponse?
        var theError: Error?

        dataTask(with: request) { (data, response, error) -> Void in

            responseData = data
            theResponse = response
            theError = error

            semaphore.signal()

        }.resume()

        _ = semaphore.wait(timeout: .distantFuture)

        if let error = theError {
            throw error
        }

        return (data: responseData, response: theResponse as! HTTPURLResponse?)

    }

}

次に、単に呼び出します:

let (data, response) = try URLSession.shared.synchronousDataTask(with: request)
9
Matej Ukmar

バックグラウンドスレッドでは、同期要求が適切な場合があります。非同期リクエストなどでいっぱいのコードベースを変更するのが複雑で不可能な場合があります。その後、現在のシステムに非同期としてフォールドできない小さなリクエストがあります。同期が失敗した場合、データは取得されません。シンプル。ファイルシステムの動作を模倣します。

確かにあらゆる種類の不測の事態をカバーしているわけではありませんが、非同期でカバーされていない不測の事態もたくさんあります。

9
Tom Andersen