0

I wrote the following code, when accessed out of closure the value of the variable "total" changes back to the initialised value i.e 4

func getTotal() -> Int{

   let task = totalDB.observe(.value) { (snapshot) in
        if let data = snapshot.value as? Dictionary<String,AnyObject> {
            let jsonData = JSON(data)

            print("\n\n\n\n",jsonData,"\n\n\n\n\n")
            print(self.total,"\n\n")
            self.total=jsonData["Total"].intValue

        }
    }

return total

}

I initialised the variable :

    var total:Int = 4
  • Your logic is a bit strange. I think that you are misunderstanding something on a conceptual level. Where is `getTotal()` called? You are both setting `self.total` and returning total... Perhaps you want to remove the return? I would also then rename the method to something like `setTotalFromDB()`. And then why do you have both a getTotal and total? Perhaps the getTotal should live in a different place instead? Then usually one would pass in an escaping closure here instead. – quin Jun 21 '18 at 17:10
  • getTotal() is called in viewDidLoad() –  Jun 21 '18 at 18:30

3 Answers3

1

If you're asking why the value returned by getTotal() might be different than what it should be according to the closure, this is almost certainly because the function is returning before the code in the closure is executing. You can confirm this in Xcode by setting a breakpoint on the self.total=jsonData... line and another on the return total line and see which breakpoint is hit first.

Nima Yousefi
  • 817
  • 6
  • 11
0

I would approach this with a completion handler.

func getTotal(_ completion: @escaping(_ error: Error?, _ value: Int?) -> Void) {
       let task = totalDB.observe(.value) { (snapshot) in
            if let data = snapshot.value as? Dictionary<String,AnyObject> {
                let jsonData = JSON(data)

                print("\n\n\n\n",jsonData,"\n\n\n\n\n")
                print(self.total,"\n\n")
                completion(nil,jsonData["Total"].intValue)

            } else { completion(NSError(domain: "Data not found", code: 0, userInfo: nil),nil) }
        }
    }

This bit of code above will get the total value and pass it through the handler. Then you can access it when you call the getTotal() function. If you get an error output you'll know that you are improperly accessing the snapshot.

Below is an example of how to use the new function:

....

// wherever you need the int value
var myNum = 0

getTotal({ (error, num) in 
    if error != nil { 
        print("error getting data", error!.localizedDescription) 
    } else { 
        myNum = num // do what you want here
    }
})
Jake
  • 2,126
  • 1
  • 10
  • 23
0

You can not have return value from asynchronous function/closure. In your case totalDB.observe(.value) will execute passed closure asynchronously and your return statement is synchronous statement, so return statement will be executed even before your closure gets executed.

What you need is a completion block

func getTotal(@escaping completionBlock : ((Int) -> ())) {
        let task = totalDB.observe(.value) {[weak self] (snapshot) in
            guard let strongSelf = self else {
                return
            }

            if let data = snapshot.value as? Dictionary<String,AnyObject> {
                let jsonData = JSON(data)
                print("\n\n\n\n",jsonData,"\n\n\n\n\n")
                print(strongSelf.total,"\n\n")
                strongSelf.total=jsonData["Total"].intValue
                completionBlock(strongSelf.total)
            }
        }

    }

Finally you can call it as

    self.getTotal() {[weak self] value in
        guard let strongSelf = self else {
            return
        }
        debugPrint("do whatever you wanna do with total here")
    }

It is not advised to pass strong self to closure, passing weak/unowned self and using guard let get safer access to self will make sure you will not end up in retain cycle/crash because of self not available

Hope this helps

Sandeep Bhandari
  • 19,999
  • 5
  • 45
  • 78