Unexpected non-void return value in void function Swift3

I have a function that returns either a class object or nil. The function's purpose is to check if a Chat exists. The chat ID's are stored in MySQL. If the ID exists, I perform a Firebase reference to get a snapshot and then get the object. If the ID does not exist, I return nil:

func findChat(string: String) -> Chat? {

    var returnValue: (Chat?)
    let url = getChatsURL
    let Parameters = [ "title" : string ]

    Alamofire.request("\(url)", method: .post, parameters: Parameters).validate().responseString { response in
            if let anyResponse = response.result.value {
                self.responseFromServer = anyResponse
            }

            if self.responseFromServer == "" {
              returnValue = nil
            } else {
                let ref = DatabaseReference.chats.reference()
                let query = ref.queryOrdered(byChild: "uid").queryEqual(toValue: (self.responseFromServer))
                query.observe(.childAdded, with: { snapshot in
                returnValue = Chat(dictionary: snapshot.value as! [String : Any])
            })
        }
        return returnValue
    }

}

However, at return returnValue I am getting

Unexpected non-void return value in void function.

Any thoughts of what I could be missing?


Solution 1:

The problem is that you are trying to return a non-void value from inside a closure, which only returns from the closure, but since that closure expects a void return value, you receive the error.

You cannot return from an asynchronous function using the standard return ... syntax, you have to declare your function to accept a completion handler and return the value from the async network call inside the completion handler.

func findChat(string: String, completion: @escaping (Chat?)->()) {
    var returnValue: (Chat?)
    let url = getChatsURL
    let Parameters = [ "title" : string ]

    Alamofire.request("\(url)", method: .post, parameters: Parameters).validate().responseString { response in
        if let anyResponse = response.result.value {
            self.responseFromServer = anyResponse
        }
        if self.responseFromServer == "" {
            completion(nil)
        } else {
            let ref = DatabaseReference.chats.reference()
            let query = ref.queryOrdered(byChild: "uid").queryEqual(toValue: (self.responseFromServer))
                query.observe(.childAdded, with: { snapshot in
                completion(Chat(dictionary: snapshot.value as! [String : Any]))
            })
        }
    }
}

Then you can call this function and use the return value like this:

findChat(string: "inputString", completion: { chat in
    if let chat = chat {
        //use the return value
    } else {
        //handle nil response
    }
})

Solution 2:

Your block is executed asynchronously, but you're trying to return a value from the enclosing function. It doesn't work that way. Your findChat function needs to take a completion block itself instead of returning a value, and then you can call that completion block from the point where you're trying to say return returnValue.