Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Swift return value from an async Void-returning block?

I want to create a function to check if user_id is already in my database.

class func checkIfUserExsits(uid:String) -> Bool {
    userRef.childByAppendingPath(uid).observeSingleEventOfType(.Value, withBlock: { (snapShot: FDataSnapshot!) -> Void in
                if snapShot.value is NSNull {
                    return false
                } else {
                    return true
                }
    })
}

However, observeSingleEventOfType is a API provided by 3rd party Firebase. It is defined to return Void.

  • (void)observeSingleEventOfType:(FEventType)eventType withBlock:(void ( ^ ) ( FDataSnapshot *snapshot ))block

Error: Type 'Void' does not conform to protocol 'BooleanLiteralConvertible'

Appreciate any kind of helps.


UPDATE

I am trying a different way:

class func checkIfExist(uid: String) -> Bool {
    var answer:Bool = false
    var text:String = "not yet completed"
    let queue = dispatch_group_create()
    dispatch_group_enter(queue)
        userRef.childByAppendingPath(uid).observeSingleEventOfType(.Value, withBlock: { (snapShot: FDataSnapshot!) -> Void in
                if snapShot.value is NSNull {
                    text = "This is a new user"
                    answer  = false
                    dispatch_group_leave(queue)
                } else {
                    text = "Found the user in Firebase"
                    answer = true
                    dispatch_group_leave(queue)
                }
        })
    dispatch_group_wait(queue, DISPATCH_TIME_FOREVER)
    println(text)
    return answer
}

Somehow it just freeze there. I know this approach could be off-topic now. But please help.

like image 872
Ivan Wang Avatar asked Feb 08 '15 04:02

Ivan Wang


1 Answers

You should employ asynchronous (ie, escaping) completion handler yourself:

class func checkIfUserExists(uid: String, completion: @escaping (Bool) -> Void) {
    userRef.childByAppendingPath(uid).observeSingleEventOfType(.Value) { snapShot in
        if snapShot.value is NSNull {
            completion(false)
        } else {
            completion(true)
        }
    }
}

You can then call this like so:

MyClass.checkIfUserExists(uid) { success in
    // use success here
}

// but not here

In your revised question, you demonstrate the use of dispatch groups to make this asynchronous method behave synchronously. (Semaphores are also often used to the same ends.)

Two issues:

  1. This will deadlock if they dispatch their completion handler back to the main queue (and in many cases, libraries will do this to simplify life for us), because you're coincidentally blocking the very same thread they're trying to use. I don't know if that's what they've done here, but is likely.

    If you want to confirm this, temporarily remove dispatch group and then examine NSThread.isMainThread and see if it's running in main thread or not.

  2. You never should block the main thread, anyway. They provided an asynchronous interface for good reason, so you should use asynchronous patterns when calling it. Don't fight the asynchronous patterns, but rather embrace them.

like image 175
Rob Avatar answered Nov 04 '22 06:11

Rob