2014-11-22 6 views
7

Come potete vedere, sto ricevendo un file JSON, analizzando usando SwiftyJSON e cercando di restituire totalTime, ma non me lo consente. Come faccio a fare questo?Swift: Come posso restituire un valore all'interno di una funzione di cancellazione asincrona?

func googleDuration(origin: String, destination: String) -> Int{ 
    // do calculations origin and destiantion with google distance matrix api 

    let originFix = origin.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil); 
    let destinationFix = destination.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil); 

    let urlAsString = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+originFix+"&destinations="+destinationFix; 
    println(urlAsString); 

    let url = NSURL(string: urlAsString)! 
    let urlSession = NSURLSession.sharedSession() 

    let task = urlSession.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in 
     if error != nil { 
      // If there is an error in the web request, print it to the console 
      println(error.localizedDescription) 
     } 

     println("parsing JSON"); 
     let json = JSON(data: data); 
     if (json["status"].stringValue == "OK") { 
      if let totalTime = json["rows"][0]["elements"][0]["duration"]["value"].integerValue { 
       println(totalTime); 
      } 
     } 
    }) 
    task.resume(); 
} 
+0

btw, piuttosto che sostituire gli spazi con '+', si potrebbe considerare di fare una vera e propria fuga per cento, come descritto qui http://stackoverflow.com/a/24888789/1271826 – Rob

risposta

9

Si dovrebbe aggiungere la tua parametro completionHandler chiusura e lo chiamano quando il compito completa:

func googleDuration(origin: String, destination: String, completionHandler: (Int?, NSError?) -> Void) -> NSURLSessionTask { 
    // do calculations origin and destiantion with google distance matrix api 

    let originFix = origin.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil); 
    let destinationFix = destination.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil); 

    let urlAsString = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+originFix+"&destinations="+destinationFix 
    println(urlAsString) 

    let url = NSURL(string: urlAsString)! 
    let urlSession = NSURLSession.sharedSession() 

    let task = urlSession.dataTaskWithURL(url) { data, response, error -> Void in 
     if error != nil { 
      // If there is an error in the web request, print it to the console 
      // println(error.localizedDescription) 
      completionHandler(nil, error) 
      return 
     } 

     //println("parsing JSON"); 
     let json = JSON(data: data) 
     if (json["status"].stringValue == "OK") { 
      if let totalTime = json["rows"][0]["elements"][0]["duration"]["value"].integerValue { 
       // println(totalTime); 
       completionHandler(totalTime, nil) 
       return 
      } 
      let totalTimeError = NSError(domain: kAppDomain, code: kTotalTimeError, userInfo: nil) // populate this any way you prefer 
      completionHandler(nil, totalTimeError) 
     } 
     let jsonError = NSError(domain: kAppDomain, code: kJsonError, userInfo: nil) // again, populate this as you prefer 
     completionHandler(nil, jsonError) 
    } 
    task.resume() 
    return task 
} 

Mi piacerebbe anche avere questo restituire il NSURLSessionTask nel caso in cui il chiamante vuole essere in grado di annullare la compito.

In ogni caso, si chiamerebbe questo modo:

googleDuration(origin, destination: destination) { totalTime, error in 
    if let totalTime = totalTime { 
     // use totalTime here 
    } else { 
     // handle error  
    } 
} 
+0

C'è un modo per rendere questo non asincrono? Ho bisogno di aspettare fino a quando non viene analizzata una risposta, ma non so come farlo. La tua soluzione era giusta, penso che la mia domanda fosse semplicemente sbagliata – teo751

+0

Tu _can, _ ma è seriamente disapprovato. È un UX orribile e se lo fai nel momento sbagliato, il processo di watchdog potrebbe uccidere in modo sommario la tua app. La soluzione corretta consiste nel disabilitare l'interfaccia utente, presentare lo spinner (in iOS, un 'UIActivityIndicatorView') e invertire tale processo quando viene eseguita la richiesta. Dovresti davvero abbracciare il modello asincrono piuttosto che combatterlo. – Rob

+0

A proposito, spesso le persone invieranno i gestori di completamento alla coda principale (perché di solito il chiamante sta effettuando gli aggiornamenti dell'interfaccia utente). Se vuoi che aggiorni la risposta per mostrarti come farlo, fammi sapere. Probabilmente è auto-esplicativo se sei esperto di GCD, ma sono felice di mostrarti, se non lo sei. – Rob

2

Un altro esempio:

class func getExchangeRate(#baseCurrency: String, foreignCurrency:String, completion: ((result:Double?) -> Void)!){ 
    let baseURL = kAPIEndPoint 
    let query = String(baseCurrency)+"_"+String(foreignCurrency) 

    var finalExchangeRate = 0.0 
    if let url = NSURL(string: baseURL + query) { 
     NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in 

      if ((data) != nil) { 
       let jsonDictionary:NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSDictionary 

       if let results = jsonDictionary["results"] as? NSDictionary{ 
        if let queryResults = results[query] as? NSDictionary{ 
         if let exchangeRate = queryResults["val"] as? Double{ 
          let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT 
          dispatch_async(dispatch_get_global_queue(priority, 0)) { 
           dispatch_async(dispatch_get_main_queue()) { 
            completion(result: exchangeRate) 
           } 
          } 

         } 
        } 
       } 
      } 
      else { 
       completion(result: nil) 
      } 

     }.resume() 
    } 
} 

chiamata:

Currency.getExchangeRate(baseCurrency: "USD", foreignCurrency: "EUR") { (result) -> Void in 
     if let exchangeValue = result { 
      print(exchangeValue) 
     } 
    } 
0

Un altro esempio:

func getJason(url: NSURL, completionHandler: (String?, NSError?) -> Void) -> NSURLSessionTask { 

    var finalData: String! 
    let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in 

     if error != nil{ 

      completionHandler(nil, error) 
      return 
     } 
     else{ 

     if let urlContent = data{ 

      do{ 
       let jsonData = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers) 

       if let ip = jsonData["ip"]{ 

        finalData = ip as? String 
        completionHandler(finalData, nil) 
        return 
       } 

      }catch{ 
       print("EMPTY") 
      } 

     } 

    } 
} 
    task.resume() 
    return task 
} 

poi ho chiamato nel viewDidLoad

getJason(url) { (ipAddress, error) -> Void in 

     if error != nil{ 

      print(error) 
     } 
     else{ 
      if let ip = ipAddress{   //To get rid of optional 

     self.ipLabelDisplay.text = "Your Ip Address is: \(ip)" 

      } 

     } 
    }