首页 > 解决方案 > 目前如何在 Swift 中进行观察?

问题描述

我在这段代码中有一个问题:

    func calculaGastos() -> Float{
    let idUser = Auth.auth().currentUser?.displayName
    var total : Float = 0
    let ref = Database.database().reference().child("Users").child(idUser!).child("Gastos")
    ref.observeSingleEvent(of: .value) { (snapshot) in
        let value = snapshot.value as? NSDictionary
        if(value != nil){
            for i in value!{
                let j = i.value as? NSDictionary
                let precio = j?["precio"] as? Float
                let fecha = j?["Fecha"] as? String
                let dateFormatter = DateFormatter()
                dateFormatter.dateFormat = "MMM d, yyyy"
                let date = dateFormatter.date(from: fecha!)
                if(((date?.timeIntervalSinceNow)! * -1) < (30*24*3600)){
                    print("Total:" ,total)
                    total += precio!

                }
            }
        }
        print("Calculing in the function", total)

    }
    return total
}

在另一个视图控制器的覆盖函数中调用它,日志显示在 viewdidload 的打印中为 0,但在函数 print 中显示打印为 30 但始终返回 0,我认为问题在于它在进入观察者之前返回,但我不确定有什么解决方案?

    override func viewDidLoad() {
    super.viewDidLoad()
    nomUser.text = id?.displayName!
    correoLabel.text = id?.email!
    print("Calculing in View Controller", calculo.calculaBenef(), calculo.calculaGastos())
    gastosField.text = String(calculo.calculaGastos())
    benefField.text = String(calculo.calculaBenef())
    // Do any additional setup after loading the view.
}

这是我的日志: 日志

标签: iosswiftfirebasefirebase-realtime-databaseswift4

解决方案


在我目前正在开发的应用程序中,我遇到了类似的问题。解决方案是在函数中实现一个调度组。我还更改了函数返回的方式total,以便它现在由完成处理程序返回。

试试这个:

 func calculaGastos(completionHandler: @escaping (Float) -> Void){
        let idUser = Auth.auth().currentUser?.displayName
        var total : Float = 0
        let ref = Database.database().reference().child("Users").child(idUser!).child("Gastos")
        ref.observeSingleEvent(of: .value) { (snapshot) in
            let value = snapshot.value as? NSDictionary
            if(value != nil){
                let myGroup = DispatchGroup()
                for i in value!{
                    myGroup.enter()
                    let j = i.value as? NSDictionary
                    let precio = j?["precio"] as? Float
                    let fecha = j?["Fecha"] as? String
                    let dateFormatter = DateFormatter()
                    dateFormatter.dateFormat = "MMM d, yyyy"
                    let date = dateFormatter.date(from: fecha!)
                    if(((date?.timeIntervalSinceNow)! * -1) < (30*24*3600)){
                        print("Total:" ,total)
                        total += precio!
                }
                     myGroup.leave()
                }
                myGroup.notify(queue: .main) {
                       print("Calculing in the function", total)
                   completionHandler(total)
               }
                }}
    }

调用函数并total像这样使用:

   override func viewDidLoad() {
    super.viewDidLoad()
    nomUser.text = id?.displayName!
    correoLabel.text = id?.email!
    print("Calculing in View Controller", calculo.calculaBenef())
    calculo.calculaGastos { (total) in
      print("calculaGastos total: \(total)")
         gastosField.text = String(total)
         benefField.text = String(calculo.calculaBenef())
    }
    // Do any additional setup after loading the view.
}

据我了解:

observeSingleEventreturn是异步的,因此它可能会在调用时完成,也可能不会完成。此外,for i in value仅在observeSingleEvent完成后才开始,因此return更有可能在任务完成之前被调用。这就是DispatchGroup()完成处理程序的用武之地。

myGroup.enter()被调用时,它会通知 DispatchGroup 一个任务已经开始。调用时myGroup.leave(),会通知 DispatchGroup 任务已完成。一旦有 s 和 s 一样多.leave().enter()这个组就结束了。然后myGroup通知主队列该组已完成,然后completionHandler调用返回总计。

由于您使用calculaGastos. 您正在调用该函数,然后您将使用返回值显示在文本字段中。现在添加了完成处理程序,textField.text只有在完成后才设置calculaGastos并返回total

calculo.calculaGastos { (total) in
      print("calculaGastos total: \(total)")
         gastosField.text = String(total)
         benefField.text = String(calculo.calculaBenef())
    }

希望这有点道理!很高兴代码对你有用。


推荐阅读