da1lbi3 da1lbi3 - 6 months ago 60
Swift Question

Return values from completion handler

I want to return the values from an api call.

The call to my api class (I want to get the values in res):

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let t = Api_test();
let res = t.getSomething();

print(res)
}
}


The api class:

import Foundation

class Api_test {

func getAllStations(completionHandler: (response : XMLIndexer) -> ()) {

getRequest { result in
completionHandler(response: SWXMLHash.parse(result))
};
}

func getRequest(completionHandler: (result: NSData) -> ()) {
let baseUrl = "http://api.test.com"
let request = NSMutableURLRequest(URL: NSURL(string: baseUrl)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "GET"

let task = session.dataTaskWithRequest(request) {
(data, response, error) in

if data == nil {
print("dataTaskWithRequest error: \(error)")
return
} else {
completionHandler(result: data!)
}
}
task.resume()
}
}


Everything works as thought, but I'm stuck at the point to return the values back to the getSomething function. The data is in xml format. How can I get the result set as returned values in the res (viewDidLoad)?

Answer

NSURLSession is a fully asynchronous networking API so ideally your view controller should operate correctly and not wait for the data to be returned from the network.

You have three options here:

  1. You can pass a completion block to getSomething and have it pass the result to the block:

    func getSomething(completionHandler: (result: XMLIndexer) -> ()) {
      getRequest { result in
        completionHandler(result: SWXMLHash.parse(result))
      }
    }
    
    override func viewDidLoad() {
      ...
      t.getSomething { res in
        print(res)
      }
    }
    
  2. If you desperately need the XML data in hand before view is displayed onto screen, you can make the main thread wait till network operation finishes executing. You can use dispatch_semaphore_t:

    func getSomething() -> XMLIndexer? {
      var xml: XMLIndexer? = nil
      let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0)
      getRequest { result in
        xml = SWXMLHash.parse(result)
        dispatch_semaphore_signal(semaphore)
      }
      dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
      return xml
    }
    
  3. Last option is, you can use another 3rd party that does the parsing synchronously. There is a great one named Ono:

    var error: NSError?
    let xml = ONOXMLDocument(data: result, error: &error)
    
Comments