2

I'm trying to return a value for a function from inside of a nested function. I understand why it doesn't work, but I just need to know a workaround for my type of problem.

In my code, I'm checking whether or not a person has already been created inside of my Firebase Database. So inside of the usersRef function, you'll see that if the value return from firebase is "nil" then the user hasn't been created and vice versa.

When I say : return userBool it's giving me this error:

Unexpected non-void return value in void function

Been looking for a clean workaround, but the only thing I can think of is having to create this function over and over again for each of the various times I want to check it. Obviously, I'd rather not do that, haha, so if you could help me out by giving some suggestions on how to fix this and have a clean solution, let me know! :)

func checkIfUserExists(userID : String) -> Bool {
    var userBool = Bool()
    var usersRef = FIRDatabase.database().reference()
    usersRef.child("users").child(userID).observeSingleEvent(of: .value, with: {
    snapshot in
        if snapshot.value != nil {
            userBool = true
            return userBool
        }
        else {
            userBool = false
            return userBool
        }
    })
}

UPDATE :

Thanks to your suggestions, I added a completion, and it worked perfectly. :)

func checkIfUserExists(userID : String, completion: @escaping (_ success: Bool) -> ()){
    var userBool = Bool()
    var usersRef = FIRDatabase.database().reference()
    usersRef.child("users").child(userID).observeSingleEvent(of: .value, with:{
        snapshot in
            if snapshot.value != nil {
                print("userBool!", userBool)
                userBool = true
            }
            else {
                print("userBool2!", userBool)
                userBool = false
            }
        completion(userBool)
    })
}

4 Answers 4

2

There is no way to return from the outer function from within the inner one. The only way I can think of is by using a completion handler, that you can then call from within the nested function like so:

func checkIfUserExists(userID : String, completion: @escaping (_ userExists: Bool) -> Void)
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the suggestion, I'll look into it. :)
Worked perfectly! Thanks!
Great! I'm glad it helped. :)
1

The problem is you're in a closure, that doesn't expect a return type.. How are you call this function?

Some ideas: * Pass a closure to this function, which then gets called, instead of trying to return.

  • Keep a mapping of the results somewhere, update that from the closure and always check against that mapping.

Also, it looks like instead of

if snapshot.value != nil {
        userBool = true
        return userBool
    }
    else {
        userBool = false
        return userBool
    }

You could just do this:

guard snapshot = snapshot else { return }
return snapshot.value

1 Comment

Thanks for your suggestions! Keeping a mapping of the results doesn't seem like the best solution for myself as the function should only be called once. I'll look into passing a closure, thanks for that idea. :)
1

The only way to make it return what you want is possible if the observeSingleEvent(...) function executes the closure synchronously to its call.

If it's true, you can try the following:

func checkIfUserExists(userID : String) -> Bool {
    var userBool = false
    var usersRef = FIRDatabase.database().reference()
    usersRef.child("users").child(userID).observeSingleEvent(of: .value, with: {
        userBool = $0.value != nil
    })
    return userBool
}

If the closure of observeSingleEvent(...) is called after a while, return a result of your method in a closure as Moritz suggested, not as a return value.

3 Comments

Unfortunately it's not true, observeSingleEvent works asynchronously, so the function will always return false.
Then there's no other option. Instead of returning a Bool there should be a completion closure as other guys suggested.
Thanks for your suggestion, and yep, as stated it's asynchronous, but I got it working by adding a closure. Thanks for the input! :)
0

Another way is to create a delegate protocol and call it inside the closure

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.