2014-11-06 4 views
44

Posso in qualche modo eseguire una richiesta HTTP sincrona tramite NSURLSession in Swift?Posso in qualche modo eseguire una richiesta HTTP sincrona tramite NSURLSession in Swift

posso fare una richiesta asincrona tramite il seguente codice:

if let url = NSURL(string: "https://2ch.hk/b/threads.json") { 
      let task = NSURLSession.sharedSession().dataTaskWithURL(url) { 
       (data, response, error) in 

       var jsonError: NSError? 
       let jsonDict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as [String: AnyObject] 
       if jsonError != nil { 
        return 
       } 

       // ... 
      } 
      task.resume() 
     } 

Ma per quanto riguarda richiesta sincrona?

Grazie in anticipo.

+0

check this out http://stackoverflow.com/questions/21198404/nsurlsession-with-nsblockoperation-and-queues – Ian

risposta

-2

Fare attenzione alle richieste sincrone perché può causare un'esperienza utente negativa ma a volte so che è necessaria. Per richieste sincrone usano NSURLConnection: "The Eskimo"

func synchronousRequest() -> NSDictionary { 

     //creating the request 
     let url: NSURL! = NSURL(string: "exampledomain/...") 
     var request = NSMutableURLRequest(URL: url) 
     request.HTTPMethod = "GET" 
     request.addValue("application/json", forHTTPHeaderField: "Content-Type") 


     var error: NSError? 

     var response: NSURLResponse? 

     let urlData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error) 

     error = nil 
     let resultDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(urlData!, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary 

     return resultDictionary 
    } 
+2

questo non sta lavorando su Swift 2 più. Apparentemente Apple ha deciso che non abbiamo bisogno di richieste sincrone. – SpaceDog

+0

@SpaceDog grazie per il tuo commento. Potresti spiegare perché Apple lo fa e dicci il "modo giusto" o fornirti qualche utile tutorial o documentazione? Grazie –

26

Apple thread discussing the same issue.

+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request 
    returningResponse:(__autoreleasing NSURLResponse **)responsePtr 
    error:(__autoreleasing NSError **)errorPtr { 
    dispatch_semaphore_t sem; 
    __block NSData *  result; 

    result = nil; 

    sem = dispatch_semaphore_create(0); 

    [[[NSURLSession sharedSession] dataTaskWithRequest:request 
     completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
     if (errorPtr != NULL) { 
      *errorPtr = error; 
     } 
     if (responsePtr != NULL) { 
      *responsePtr = response; 
     } 
     if (error == nil) { 
      result = data; 
     } 
     dispatch_semaphore_signal(sem); 
    }] resume]; 

    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 

    return result; 
} 

Risposta da Quinn Di Apple Developer Relations, sviluppatori supporto tecnico, Core OS/hardware

50

È possibile utilizzare questa estensione NSURLSession per aggiungere un metodo sincrono:

extension NSURLSession { 
    func synchronousDataTaskWithURL(url: NSURL) -> (NSData?, NSURLResponse?, NSError?) { 
     var data: NSData?, response: NSURLResponse?, error: NSError? 

     let semaphore = dispatch_semaphore_create(0) 

     dataTaskWithURL(url) { 
      data = $0; response = $1; error = $2 
      dispatch_semaphore_signal(semaphore) 
     }.resume() 

     dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 

     return (data, response, error) 
    } 
} 

Aggiornamento per Swift 3:

extension URLSession { 
    func synchronousDataTask(with url: URL) -> (Data?, URLResponse?, Error?) { 
     var data: Data? 
     var response: URLResponse? 
     var error: Error? 

     let semaphore = DispatchSemaphore(value: 0) 

     let dataTask = self.dataTask(with: url) { 
      data = $0 
      response = $1 
      error = $2 

      semaphore.signal() 
     } 
     dataTask.resume() 

     _ = semaphore.wait(timeout: .distantFuture) 

     return (data, response, error) 
    } 
} 
+1

Ho aggiunto questa estensione alla fine del mio file e ho sostituito 'NSURLSession.sharedSession(). DataTaskWithURL (url)' con 'NSURLSession.sharedSession(). SynchronousDataTaskWithURL (url)', ma sto ricevendo un errore che c'è un argomento in più in chiamata. Qual è il modo corretto per chiamare questa funzione di estensione? – tylerSF

+2

Quello che hai scritto è il modo corretto per chiamarlo, probabilmente l'errore è altrove. Puoi fornire più contesto? –

+1

L'argomento aggiuntivo è il gestore di completamento, che non deve più essere parametrizzato nella chiamata sincrona. La chiamata a questo metodo dovrebbe apparire come segue (assumendo che tu abbia dichiarato le variabili richieste): '(dati, risposta, errore) = NSURLSession.sharedSession(). SynchronousDataTaskWithURL (url)' – dave

7

Aggiornamento di una delle risposte per utilizzare invece una richiesta URLRequest, in modo che possiamo utilizzare PUT, ecc.

extension URLSession { 
    func synchronousDataTask(urlrequest: URLRequest) -> (data: Data?, response: URLResponse?, error: Error?) { 
     var data: Data? 
     var response: URLResponse? 
     var error: Error? 

     let semaphore = DispatchSemaphore(value: 0) 

     let dataTask = self.dataTask(with: urlrequest) { 
      data = $0 
      response = $1 
      error = $2 

      semaphore.signal() 
     } 
     dataTask.resume() 

     _ = semaphore.wait(timeout: .distantFuture) 

     return (data, response, error) 
    } 
} 

Sto chiamando così.

var request = URLRequest(url: url1) 
request.httpBody = body 
request.httpMethod = "PUT" 
let (_, _, error) = URLSession.shared.synchronousDataTask(urlrequest: request) 
if let error = error { 
    print("Synchronous task ended with error: \(error)") 
} 
else { 
    print("Synchronous task ended without errors.") 
}