我将Google Firestore用作我正在制作的iOS应用程序的后端服务器。我正在尝试结合一个倒数计时器来表示游戏何时开始,但是至关重要的是,使用网络时间来反对设备时间,以确保每个人都同时开始游戏。
基本思想是从Firebase检索当前时间戳,从我希望游戏启动时的另一个时间戳值中减去它,然后继续每秒刷新按钮文本(不是标签,因为我需要能够按一下它揭示更多信息)与时差。
当应用启动时,计时器运行得很好,没有/只有很小的延迟,但是,如果我更改了游戏开始时的值,则按钮文本会由于某些原因而出现故障。即使在调试区域,我也看到它滞后。
我已经完成了所有的数学和转换。当前时间戳是一个timeIntervalSince1970值,我将其转换为格式为“ HH:mm:ss”的字符串。我正在从Firebase服务器值中检索此值。我希望游戏开始的另一个时间戳是我存储在Firestore集合中的字符串值。
我将这两个字符串值发送到一个为我找到差异的函数,然后将按钮文本设置为该值。很简单,但我不明白为什么更改第二个时间戳值时它开始滞后。如果我第三次更改它,该应用程序仍会运行,但计时器基本上每8秒左右冻结一次。
覆盖func viewDidAppear(_动画:布尔){
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 &&秒<10 {
self.time.setTitle(("\(Int(hours)):\(Int(minutes)):0\(Int(seconds))"), for: .normal)
我希望按钮文本会以正确的倒计时时间刷新。我希望当我更改Firestore时间戳值时,按钮文本会自动刷新并且不会滞后。
实际结果是它滞后了。
答案 0 :(得分:0)
所以我不确定是否正确,但是我的理解是您要设置一个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()
}
此外,我建议将时间戳记值比较为Doubles,然后按需要格式化差异,而不是格式化每个变量,然后将其比较为字符串