Reginald
Reginald

Reputation: 113

Synchronous API request to Asynchronous API request Swift 2.2

Well I am new to Swift and I don't know much of completion handler. I want to get a request from an API and parse the JSON response so I can get the token. But what's happening with my code is that whenever I call the getAuthentication function my UI freezes and waiting for the data to get. Here is the code for getAuthentication

func getAuthentication(username: String, password: String){
    let semaphore = dispatch_semaphore_create(0);
    let baseURL = "Some URL here"
    let url = NSURL(string: baseURL)!
    let request = NSMutableURLRequest(URL: url)
    request.HTTPMethod = "POST"
    request.HTTPBody = "{\n  \"username\": \"\(username)\",\n  \"password\": \"\(password)\"\n}".dataUsingEncoding(NSUTF8StringEncoding);

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

        if error == nil{
            let swiftyJSON = JSON(data: data!)
            print(swiftyJSON)

            //parse the data to get the user
            self.id = swiftyJSON["id"].intValue
            self.token = swiftyJSON["meta"]["token"].stringValue
        } else {
            print("There was an error")
        }
      dispatch_semaphore_signal(semaphore);
    }
    task.resume()
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

then, I am calling this method in my LoginViewController. Someone says that I am using a Synchronous request thats why my UI freezes, but I have really no idea on how to change it to Async and wait for the data to be downloaded. Can someone help me with this? Any help will much be appreciated.

Upvotes: 0

Views: 1106

Answers (2)

KrishnaCA
KrishnaCA

Reputation: 5695

Firstly, remove dispatch_semaphore related code from your function.

func getAuthentication(username: String, password: String){

   let baseURL = "Some URL here"
   let url = NSURL(string: baseURL)!
   let request = NSMutableURLRequest(URL: url)
   request.HTTPMethod = "POST"
   request.HTTPBody = "{\n  \"username\": \"\(username)\",\n  \"password\": \"\(password)\"\n}".dataUsingEncoding(NSUTF8StringEncoding);

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

       if error == nil{
          let swiftyJSON = JSON(data: data!)
          print(swiftyJSON)

          //parse the data to get the user
          self.id = swiftyJSON["id"].intValue
          self.token = swiftyJSON["meta"]["token"].stringValue
       } else {
          print("There was an error")
       }
   }
   task.resume()
}

In the above code, the function dataTaskWithRequest itself is an asynchronus function. So, you don't need to call the function getAuthentication in a background thread.

For adding the completion handler,

func getAuthentication(username: String, password: String, completion:((sucess: Bool) -> Void)){

   let baseURL = "Some URL here"
   let url = NSURL(string: baseURL)!
   let request = NSMutableURLRequest(URL: url)
   request.HTTPMethod = "POST"
   request.HTTPBody = "{\n  \"username\": \"\(username)\",\n  \"password\": \"\(password)\"\n}".dataUsingEncoding(NSUTF8StringEncoding);

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

       var successVal: Bool = true

       if error == nil{
          let swiftyJSON = JSON(data: data!)
          print(swiftyJSON)
          self.id = swiftyJSON["id"].intValue
          self.token = swiftyJSON["meta"]["token"].stringValue
       } else {
          print("There was an error")
          successVal = false
       }

       dispatch_async(dispatch_get_main_queue(), { () -> Void in
          completion(successVal)                 
       })
   }
   task.resume()
}

It can be called as follows:

self.getAuthentication("user", password: "password", completion:  {(success) -> Void in

})

Upvotes: 2

ikbal
ikbal

Reputation: 31

You may pass an escaping closure argument to getAuthentication method.

func getAuthentication(username: String, password: String, completion: (JSON) -> ()){
    ...
    // create a request in the same way
    ...
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
        if error == nil{
            let swiftyJSON = JSON(data: data!)
            print(swiftyJSON)
            completion(swiftyJSON)
        } else {
            print("There was an error")
        }
    }
    task.resume()
}

And call getAuthentication in LoginViewController like this:

getAuthentication(username, password) { (json) -> in
    //Do whatever you want with the json result
    dispatch_async(dispatch_get_main_queue()) {
        // Do UI updates
    }
}

Another way to go is calling getAuthentication in a background thread in your LoginViewController to avoid blocking the main thread (i.e. UI thread).

//In LoginViewController   
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
      getAuthentication(username, password)
      dispatch_async(dispatch_get_main_queue()) {
         // UI updates
     }
}

Upvotes: 1

Related Questions