How to use callbacks to make Asynchronous calls in Swift so that main UI does not Hang?

4.2k views Asked by At

I am trying to make Asynchronous call (to Parse) and during the call I DON'T want my Main UI to get hung.

Below is the function I am trying to call my ViewController. Below that function is the code I am using from my ViewController.

In the line sleep(4) line the main UI of the ViewController LoginVC does get stuck. Am I not using callbacks for the Asynchronous call correctly?

class Utils {

func logIntoWebService(uname: String, pwd: String, completion: ((result:Bool?) -> Void)!) {

    PFUser.logInWithUsernameInBackground(uname, password:pwd) {
        (user, error) -> Void in

        if error == nil {
            if user != nil {
                // Yes, User Exists

                //UI IS STUCK DURING THIS SLEEP(10)
                sleep(10)

                completion(result: true)

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

            completion(result: false)

        }

    }
}

}

I am calling the above function from my ViewController. Below is the call from my ViewController

class LoginVC: UIViewController {

var mUtils: Utils = Utils()

mUtils.loggedInStatus({ (result) -> Void in
        println("Response from logInToParse = " + String(stringInterpolationSegment: result))

    })

}
1

There are 1 answers

4
Icaro On BEST ANSWER

You are getting confuse between background threads and completion handler.

logInWithUsernameInBackground is a async function that runs in the background however all the code that runs in the completion handler runs back in the main thread:

 completion: ((result:Bool?) -> Void)!) {//The code here runs in the main thread}

So basically from the time that your application start communicate with Parse.com, until the result was back, that code was running asynchronous in a background thread, when it finish and your application received the response it runs in the completion block back in the main thread.

Now lets say you really want to run some code in the background in the completion block, than you would use something like:

let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
    println("Now this code will run in the background thread")
})

All the new quality of service classes are:

QOS_CLASS_USER_INTERACTIVE
QOS_CLASS_USER_INITIATED
QOS_CLASS_UTILITY
QOS_CLASS_BACKGROUND

Or in your case:

PFUser.logInWithUsernameInBackground(uname, password:pwd) {
    (user, error) -> Void in
    if error == nil {
        if user != nil {
            // Yes, User Exists

            let qualityOfServiceClass = QOS_CLASS_BACKGROUND
            let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
            dispatch_async(backgroundQueue, {
                sleep(10)
            })
            completion(result: true)

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

        completion(result: false)
    }
}

For more information see Apple documentation