-1

I need to return a value from this function and i have 2 parameters. I can not make escape function with completion. How to make it escape?

func myReturn(str:String,userCU:String)->String{
        var res = ""
        let refU = Database.database().reference(withPath: "users")
        refU.child(userCU).observeSingleEvent(of:.value) {
                (snapshot) in

               if snapshot.exists(){
                   // print(snapshot)
                    let dict = snapshot.value as! [String:Any]
                    print(dict)
                    //dict["userCity"]
                    res = dict[str] as! String
                    print(res)


                }
                else {
                    print("noooooo")
            }
        }
       print(res)

         return res
    }
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • You're saying, you want to return the final result from this function because you don't want to have a completion handler, correct? – K.Wu Jun 10 '19 at 21:25
  • @K.Wu, yes. i need to return string res – Artemoon Jun 10 '19 at 21:29
  • Not possible. Look at `refU.child(userCU).observeSingleEvent(of:.value) { ... }`, I'm assuming you're using firebase SDK. `{ ... }` is already an escaped completion handler, its return value is `Void()`, you cannot return anything from within the completion handler as a part of the function's return value, that literally contradicts the definition of `@escaping`. If you don't want to use any 3rd party library, go with completion handler. – K.Wu Jun 10 '19 at 21:35
  • You're not allowed to use an escaping closure or you don't know how to do an escaping closure? – trndjc Jun 10 '19 at 21:39
  • @bsod i don`t know – Artemoon Jun 10 '19 at 21:42
  • Data is loaded from Firebase asynchronously. By the time your `return res` runs, the code that contains `res = dict[str] as! String` hasn't run yet. You will need to either have the code that needs the results inside the closure, or pass in a custom callback as shown here: https://stackoverflow.com/questions/43534111/swift-closure-not-setting-variable – Frank van Puffelen Jun 10 '19 at 21:53
  • This isn't how Firebase works and in particular asynchronous functions. It takes time to retrieve data from Firebase and the code *outside* the closure will execute before the code *inside* the closure. While there are escaping options, those may not work well for every use case. Structure your app to work with Firebase data within the closure as that's the only time it's valid. See @FrankvanPuffelen awesome answer [here](https://stackoverflow.com/questions/56314265/how-to-display-data-from-firebase-faster/56314312#56314312) which has additional links. – Jay Jun 10 '19 at 22:00

1 Answers1

1

As a starting point, I'd consider returning an optional String in case the database call fails.

func getStringAsync(str: String, usr: String, completion: @escaping (String?) -> Void) {

    Database.database().reference(withPath: "users").child(usr).observeSingleEvent(of: .value) { (snapshot) in

        if let snapshot = snapshot,
            let d = snapshot.value as? [String: Any],
            let result = d[str] as? String {
            completion(result)
        } else {
            completion(nil)
        }

    }

}

And then to handle the optional String:

getStringAsync(str: "abc", usr: "xyz") { (str) in

    if let str = str {
        print(str)
    }

}
trndjc
  • 11,654
  • 3
  • 38
  • 51
  • I'm not sure this is going to work for the presented use case. For example, if there are three statements in an app `print("Start")` then 'getStringAsynch... and then `print("End")` the Start and End will print before the str. Or, I just may be misunderstanding how this would be used. – Jay Jun 10 '19 at 22:11
  • `getStringAsync()` has to wait for the database to return before it can return the string. As for use cases, if OP abstracted a network/data layer, like a data manager object that view controllers went through to fetch data from Firebase (instead of each view controller manually fetching from within themselves), they would all communicate with that object through these escaping closures. – trndjc Jun 10 '19 at 22:46
  • Given the question was vague, it looks like they are trying to actually *return data from a Firebase closure*, like returning a string from a synchronous function, and continue to work with the returned data after the closure. Like `let myName = getStringAsync{...}` and then `print(myName)` which obviously won't work, as I mentioned. So this is a good example of escaping a closure but I don't think that's what they were after and they also said *can not make escape function with completion*... whatever that means lol. – Jay Jun 10 '19 at 22:58