从 Firestore 检索数据时如何防止按钮文本刷新滞后?

How to prevent button text refresh lag when retrieving data from Firestore?

我正在使用 Google Firestore 作为我正在制作的 iOS 应用程序的后端服务器。我正在尝试加入一个倒数计时器来表示游戏何时开始,但是,使用网络时间而不是设备时间来确保每个人同时开始游戏是至关重要的。

基本思路是从 Firebase 检索当前时间戳,从我希望游戏开始的时间戳值中减去它,然后每秒刷新一次按钮文本(不是标签,因为我需要能够按它显示更多信息)与时差。

当应用程序启动时,计时器工作得非常好,no/very 最小延迟但是,如果我更改了我希望游戏开始的时间的值,按钮文本会出于某种原因出现故障。即使在调试区,我也能看到它的滞后。

我已经完成了所有的计算和转换。当前时间戳是一个 timeIntervalSince1970 值,我将其转换为格式为 "HH:mm:ss" 的字符串。我正在从 firebase 服务器值中检索此值。我希望游戏开始的另一个时间戳是我存储在 Firestore 集合中的字符串值。

我将这两个字符串值发送到一个函数,该函数为我找出不同之处,并将按钮文本设置为该值。这很简单,但我不明白为什么当我更改第二个时间戳值时它开始滞后。如果我第三次更改它,该应用程序仍会运行,但计时器基本上每 8 秒左右冻结一次。

重写 func viewDidAppear(_ animated: Bool) {

    DispatchQueue.main.async {

    let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in

            Firestore.firestore().collection("countdown").document("thursday")
                .addSnapshotListener { documentSnapshot, error in
                    guard let document = documentSnapshot else {
                        print("Error fetching document: \(error!)")
                        return
                    }
                    guard let theTime = document.data() else {
                        print("Document data was empty.")

                        return
                    }
                    print("Current data: \(theTime)")

            let stringData = "\(theTime)"
            let finalTime = stringData.substring(from: 9, to: 16) 

// 也许不是获得正确格式的最传统方法,但它对我有用。我当然愿意接受建议。

            var currentTimeStamp: Double?

            let ref = Database.database().reference().child("serverTimestamp")

            ref.setValue(ServerValue.timestamp())

            ref.observe(.value, with: { snap in
                if let t = snap.value as? Double {

                    currentTimeStamp = t/1000

                    let unixTimestamp = currentTimeStamp
                    let date = Date(timeIntervalSince1970: unixTimestamp!)
                    let dateFormatter = DateFormatter()
                    dateFormatter.timeZone = TimeZone(abbreviation: "EDT") //Set timezone that you want
                    dateFormatter.locale = NSLocale.current
                    dateFormatter.dateFormat = "HH:mm:ss" //Specify your format that you
                    let strDate = dateFormatter.string(from: date)

                    let dateDiff = self.findDateDiff(time1Str: strDate, time2Str: finalTime)
                    print("This is the countdown time: \(dateDiff)")

                }
            })
        }
    }
    }

// 这只是函数内部的一个峰值,它执行所有数学运算并在按钮文本中输出倒计时时间。对于计算 "HH:mm:ss" 格式的所有场景,我有很多 if 语句。

如果小时 >= 10 && 分钟 >= 10 && 秒 < 10{

        self.time.setTitle(("\(Int(hours)):\(Int(minutes)):0\(Int(seconds))"), for: .normal)

我希望按钮文本以正确的倒计时时间刷新。我希望当我更改 Firestore 时间戳值时,按钮文本会自动刷新并且不会滞后。

实际结果是滞后

所以我不确定我是否理解正确,但我的理解是您正在设置一个 Timer 对象以每秒触发一个 .observe 调用?如果是这样,您的 Firebase 调用没有及时检索数据(.observe 是异步的,所以这就是延迟的来源)。

我建议您首先设置倒计时的时间戳,然后 post 将该值设置为 Firebase。

当每个设备需要倒计时按钮时,他们检索时间戳,然后将 Timer 对象设置为以 1 秒为间隔触发,以从 NSDate().timeIntervalFrom1970 之类的东西中检索当前时间,找出差异在两者之间,然后设置按钮文本(并在倒计时结束后停用 Timer)。也许是这样的?:

override func viewDidAppear(_ animated: Bool) {

    // get time to count down from
    Firestore.firestore().collection("countdown").document("thursday").getDocument { (document, error) in

        guard let document = document, document.exists else {
            // document doesn't exist
            return
        }

        guard let theTime = document.data() else {
            print("Document data was empty.")
            return
        }

        print("Current data: \(theTime)")

        let stringData = "\(theTime)"
        let finalTime = stringData.substring(from: 9, to: 16)
        self.setTimer(for: finalTime)
    }
}

// set Timer

func setTimer(for finalTime: String) {

    let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in

        let timeNow = Date()

        let dateFormatter = DateFormatter()
        dateFormatter.timeZone = TimeZone(abbreviation: "EDT")
        dateFormatter.locale = NSLocale.current
        dateFormatter.dateFormat = "HH:mm:ss"

        let strDate = dateFormatter.string(from: timeNow)

        // stop timer once finalTime reached. Idk what finalDate is but here's if it's formatted the same as strDate is.
        guard finalTime != strDate else {
            timer.invalidate()
            print("countdown over")

            return
        }

        let dateDiff = self.findDateDiff(time1Str: strDate, time2Str: finalTime)

        print("This is the countdown time: \(dateDiff)")
    }

    timer.fire()
}

此外,我建议将时间戳值作为双精度值进行比较,然后根据需要格式化差异,而不是格式化每个变量,然后将它们作为字符串进行比较