Taco
Taco

Reputation: 700

Performing an asynchronous task within a synchronous call

I would like to register a user which is performed asynchronous. However, the calling function behaves synchronous since the program should only continue when a user is created successfully. The current implementation is:

class SignUp: NSObject {

    // ...

    func signUpUser() throws -> Bool {
        guard hasEmptyFields() else {
            throw CustomErrorCodes.EmptyField
        }

        guard isValidEmail() else {
            throw CustomErrorCodes.InvalidEmail
        }

        createUser( { (result) in
            guard result else {
                throw CustomErrorCodes.UserNameTaken
            }
            return true // Error: cannot throw....
        })
    }


    func createUser( succeeded: (result: Bool) -> () ) -> Void {
        let newUser = User()
        newUser.username = username!
        newUser.password = password!

        // User is created asynchronously
        createUserInBackground(newUser, onCompletion: {(succeed, error) -> Void in
            if (error != nil) {
                // Show error alert
            } else {
                succeeded(result: succeed)
            }
        })

    }

}

and in a ViewController the signup is initiated as follows:

do {
    try signup.signUpUser()
} catch let error as CustomErrorCodes {
    // Process error
}

However, this does not work since createUser is not a throwing function. How could I ensure that signUpUser() only returns true when an new user is created successfully?

Upvotes: 0

Views: 62

Answers (2)

matt
matt

Reputation: 535989

You say:

and in a ViewController the signup is initiated as follows:

do {
    try signup.signUpUser()
} catch let error as CustomErrorCodes {
    // Process error
}

But don't. That's not how asynchronous works. The whole idea is that you do not wait. If you're waiting, it's not asynchronous. That means you're blocking, and that's just what you mustn't do.

Instead, arrange to be called back at the end of your asynchronous process. That's when you'll hear that things have succeeded or not. Look at how a download task delegate is structured:

https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSessionDownloadTask_class/

The download task calls back into the delegate to let it know whether we completed successfully or not. That is the relationship you want to have with your asynchronous task. You want to be like that delegate.

Upvotes: 1

Duncan C
Duncan C

Reputation: 131491

You need to adjust your thinking. Instead of trying to write a synchronous method that we need to wait for an asynchronous event, write a method that takes a completion closure. The method will return immediately, but once the asynchronous process is complete it wild invoke the completion closure. When you call such a method you pass in code in the incompletion closure that gets called once the job is done.

Upvotes: 0

Related Questions