2015-07-22 5 views
16

Ich habe diesen Code aus here 2. synchrone Anforderung einer URL auf Swift zu tunSynchrone URL-Anfrage auf 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) 
    } 

aber die Funktion NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: error) war veraltet, und ich sehe nicht, wie ein Synchron tun können Anfragen an Swift, denn die Alternative ist asynchron. Anscheinend lehnte Apple die einzige Funktion ab, die es synchron tun kann.

Wie kann ich das tun?

+0

Sie in der Regel eine synchrone Web-Anfrage wirklich nicht wollen, weil machen Es könnte eine sehr lange Zeit dauern und das wäre keine nette Benutzererfahrung, deshalb wurde sie entfernt. Aber wenn Sie wirklich brauchen, können Sie meine Antwort [hier] (http://stackoverflow.com/a/30992778/3443689) ansehen und sie an Ihre Bedürfnisse anpassen.Die Funktion 'syncFromAsync' nimmt eine asynchrone Funktion und führt sie synchron aus, es gibt dort auch ein Beispiel: – Kametrixom

+0

wow, dieser Code ist ziemlich exoterisch für mich, wie uralte Klingonen! Ich lerne immer noch Swift. Habe keine Ahnung, wie ich das anpasse, um eine URL-Anfrage zu machen! Danke trotzdem. – SpaceDog

+0

Siehe http://stackoverflow.com/questions/27785168/swift-nsurlconnection-sendsynchronousrequest –

Antwort

12

Es gibt einen Grund für die Ablehnung - es gibt einfach keinen Nutzen für sie. Sie sollten synchrone Netzwerkanforderungen als Plage vermeiden. Es verfügt über zwei Hauptprobleme, und nur ein Vorteil (es ist einfach zu bedienen .. aber nicht so gut Asynchron?):

  • die Anfrage blockiert Ihre UI wenn nicht aus anderen Thread genannt, aber wenn Sie das tun, , warum verwenden Sie den asynchronen Handler nicht sofort?
  • Es gibt keine Art und Weise, wie diese Anforderung abzubrechen, außer wenn es Fehler auf eigene

Statt dessen ist nur asynchrone Anforderung verwenden:

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 Deprecation

Seit In iOS9 wird diese Methode nicht weiter unterstützt. Ich schlage vor, stattdessen NSURLSession zu verwenden:

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) 
} 
+1

diese Funktion wurde auf iOS 9 – SpaceDog

+0

veraltet Ich bearbeitete meinen Beitrag mit Ihrer Notiz. Vielen Dank –

+0

Für eine längere Exposition darüber, warum synchrone Vernetzung ist schlecht, schauen Sie auf https://devforums.apple.com/thread/9606?tstart=0 (erfordert iOS dev Foren Zugriff) –

37

Wenn Sie wirklich tun wollen es synchron können Sie immer eine Semaphore verwenden:

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

EDIT: einige hackish hinzufügen! so dass der Code funktioniert, tut dies nicht in der Produktion Code

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) 
} 
+0

Xcode 7b4 beschwert sich über viele Dinge in Ihrem Code. – SpaceDog

+0

was down vote? Ich habe Sie nicht abgelehnt. – SpaceDog

6

Synchron-Anfragen sind manchmal fein auf Hintergrund-Threads. Manchmal hat man eine komplizierte, unmögliche Codebasis voller asynchroner Anfragen usw. Dann gibt es eine kleine Anfrage, die nicht als async in das aktuelle System eingeklappt werden kann. Wenn die Synchronisierung fehlschlägt, erhalten Sie keine Daten. Einfach. Es ahmt nach, wie das Dateisystem funktioniert.

Sicher, es deckt nicht alle möglichen Eventualitäten ab, aber es gibt viele Eventualitäten, die nicht asynchron abgedeckt sind.

9

Basierend auf @ fpg1503 Antwort ich eine einfache Erweiterung in Swift 3 gemacht:

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?) 

    } 

} 

Dazu rufen Sie einfach an:

let (data, response) = try URLSession.shared.synchronousDataTask(with: request) 
+0

brilliant! Vielen Dank! – SpaceDog