【问题标题】:How to prevent button text refresh lag when retrieving data from Firestore?从 Firestore 检索数据时如何防止按钮文本刷新滞后?
【发布时间】:2019-09-09 08:00:40
【问题描述】:

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

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

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

我已经完成了所有的数学计算和转换。当前时间戳是一个 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)")

                }
            })
        }
    }
    }

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

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

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

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

实际结果是滞后了。

【问题讨论】:

    标签: swift firebase timestamp google-cloud-firestore countdown


    【解决方案1】:

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

    我建议改为首先设置您要倒计时的时间戳并将该值发布到 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()
    }
    

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

    【讨论】:

    • 啊,非常感谢。这是有道理的,但是我唯一的问题是,如果我想更改游戏通过 Firestore 启动的时间,我的应用程序将无法识别更改,因为检索该值的代码部分位于 viewDidAppear 中,因此只能读取一次.我需要找到一种方法,我可以随时更改时间值,并立即在应用程序中反映,没有延迟。
    • @TNasty 就像我说的,从 Firestore 获取游戏计划开始时间的时间戳,然后设置计时器以找出当前时间 (Date()) 与每秒时间戳之间的差异.例如:假设您希望游戏从时间戳 1555710183 开始。您打开视图,从 viewDidAppear 中的 Firestore 获取该时间戳值并将其存储在 startTime 中,并设置一个 Timer 来计算差异 = finalTime - NSDate().timeIntervalSince1970 每秒. Timer 每秒都会触发并为您提供一个新值,直到它在该时间结束时失效。
    • @TNasty 如果我错了,请纠正我:您正在尝试制作倒计时视图以倒计时到游戏开始的时间,对吗?除非您希望游戏在不同的时间开始,否则您不应不断更改该值。如果您需要推迟开始时间或其他什么,请将 .getDocument() 调用更改为将不断观察该值的内容,因此当值更改时,您可以将 finalTime 变量设置为不同的值,计时器会注意到它并走得更久。
    • 我修好了!因此,感谢您最后的澄清答复;我可能需要更改开始时间,因此我最终使用了 onSnapshot() 方法,以便它观察我的所有更改(不知道为什么以前没有打到我)。然后,我将 Timer 从 setTime 函数移到 viewDidAppear 的开头,这样每一秒,我都可以查看 Firebase 中的开始时间是否有任何变化。然后将该值发送到 setTime 函数以进行所有数学计算和转换。再次感谢您!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-04
    • 2015-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多