(Swift)后台计时器使用什么API实现?

(Swift)后台计时器使用什么API实现?,swift,timer,background,countdown,Swift,Timer,Background,Countdown,考虑一下像Clash Royale(以及无数其他)这样的应用程序,它有一个奖励倒计时计时器,每隔一段时间就会产生奖励或其他游戏内货币,这是如何做到的 我当前的过程是从谷歌获取当前日期和时间,当应用程序进入任何类型的背景状态(或终止)时,将其上传到Firebase中的用户数据,然后在重新启动应用程序时,它再次读取当前日期和时间,并将其与Firebase中的旧值进行比较。。。然后我做一些数学计算,计算出计时器应该在什么位置,以及在这段时间内赚了多少钱。。。但这感觉超级叮当 还有什么东西可以处理这种背

考虑一下像Clash Royale(以及无数其他)这样的应用程序,它有一个奖励倒计时计时器,每隔一段时间就会产生奖励或其他游戏内货币,这是如何做到的

我当前的过程是从谷歌获取当前日期和时间,当应用程序进入任何类型的背景状态(或终止)时,将其上传到Firebase中的用户数据,然后在重新启动应用程序时,它再次读取当前日期和时间,并将其与Firebase中的旧值进行比较。。。然后我做一些数学计算,计算出计时器应该在什么位置,以及在这段时间内赚了多少钱。。。但这感觉超级叮当

还有什么东西可以处理这种背景倒计时吗


非常感谢

对我来说,这听起来确实是正确的方法,我过去也做过类似的事情。我会按照你的方式进行,直到有人能给出更好的答案……

我同意你的看法。我看你的设计没有什么大问题。有很多方法可以通过直接攻击程序来击败它,但每种解决方案都是如此。您的简单、优雅且有效地抵御了大多数简单的攻击(解决复杂的攻击要困难得多,而且可能不值得)。这肯定是我的第一选择

我建议使用证书固定。否则,很容易欺骗程序读取错误的时间服务器。证书固定告诉你的应用程序只信任一个特定的HTTPS证书或一组证书,而不仅仅是设备信任的每个证书。有关介绍,请参阅。有关简化ObjC实现的信息,请参阅

您的方法的一个可用性问题是,它需要网络连接才能正常工作。如果用户没有网络访问权限,您是否会阻止他们使用该程序(可能由于其他原因这已经是事实),或者您是否认为他们的本地时钟是正确的(这可能会破坏整个目的)


如果需要脱机访问,您可以利用的工具是
mach\u absolute\u time
。这比您的方法更复杂,更难保护,但它可以脱机工作<代码>马赫绝对时间返回单调递增的时钟。也就是说,它总是以恒定的速率上升,即使用户修改了系统时钟。然而,它不是以秒为单位的。您需要使用
AbsoluteToDuration
来转换它。(有关详细信息,请参阅。)当设备重新启动时,它会重置。原则上,您可以将它提供的时间与系统时钟提供的时间进行比较,以确定系统时钟是否可靠,如果可靠,您可以在脱机时奖励分数。这是否值得额外的工作(并且它仍然可以有假底片)取决于你的用例。

< P>基础库包含一个<代码>定时器类,你可以阅读文档。

它提供了一个API,用于在经过一定时间后启动工作。要使用的构造函数方法是:

class func scheduledTimer(withTimeInterval:TimeInterval,repeats:Bool,block:(Timer)->Void)->Timer

(因为另一个版本使用选择器,如果可以避免的话,谁想在编写Swift时编写拙劣的Objective-C?)

举个例子,假设你有

  • 显示剩余秒数的标签
    secondsLeftLabel
  • 完成后要调用的函数
    timesUp()
  • 首先,需要创建计时器属性和第二个计时器属性:

    private var timer: Timer?
    private var secondsLeftInTimer: Double = 0
    
    然后您可以实现如下功能:

    func setUpTimer(numberOfSeconds: Double) {
        secondsLeftInTimer = numberOfSeconds
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (_) in
            self?.secondElapsed()
        }
    }
    
    func secondElapsed() {
        secondsLeftInTimer -= 1
        secondsLeftLabel.text = "\(secondsLeftInTimer) seconds left"
    
        if secondsLeftInTimer <= 0 {
            timer.invalidate()
            timer = nil
            timesUp()
        }
    }
    
    func setUpTimer(numberOfSeconds: Int) {
        secondsLeftInTimer = Double(exactly: numberOfSeconds)
    
    func scheduleNotificationAfter(numberOfSeconds: Double) {
        let content = UNMutableNotificationContent()
        content.title = "Time's up!"
    
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: numberOfSeconds, repeats: false)
    
        let notification = UNNotificationRequest(identifier: "Times Up Notification", content: content, trigger: trigger)
    
        UNUserNotificationCenter.current().add(notification, withCompletionHandler: nil)
    }
    
    您将看到我创建了一个helper函数,您将实现如下内容:

    func setUpTimer(numberOfSeconds: Double) {
        secondsLeftInTimer = numberOfSeconds
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (_) in
            self?.secondElapsed()
        }
    }
    
    func secondElapsed() {
        secondsLeftInTimer -= 1
        secondsLeftLabel.text = "\(secondsLeftInTimer) seconds left"
    
        if secondsLeftInTimer <= 0 {
            timer.invalidate()
            timer = nil
            timesUp()
        }
    }
    
    func setUpTimer(numberOfSeconds: Int) {
        secondsLeftInTimer = Double(exactly: numberOfSeconds)
    
    func scheduleNotificationAfter(numberOfSeconds: Double) {
        let content = UNMutableNotificationContent()
        content.title = "Time's up!"
    
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: numberOfSeconds, repeats: false)
    
        let notification = UNNotificationRequest(identifier: "Times Up Notification", content: content, trigger: trigger)
    
        UNUserNotificationCenter.current().add(notification, withCompletionHandler: nil)
    }
    

    起初我没有意识到你的意思是真正的背景(比如当应用程序是背景的时候),所以我的另一个答案不起作用。对于您想要做的事情,这是计划好的未通知的适当用例

    从iOS 10开始,您可以使用自定义触发器安排本地通知。一个这样的触发器是时间间隔。你可以这样做:

    func setUpTimer(numberOfSeconds: Double) {
        secondsLeftInTimer = numberOfSeconds
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (_) in
            self?.secondElapsed()
        }
    }
    
    func secondElapsed() {
        secondsLeftInTimer -= 1
        secondsLeftLabel.text = "\(secondsLeftInTimer) seconds left"
    
        if secondsLeftInTimer <= 0 {
            timer.invalidate()
            timer = nil
            timesUp()
        }
    }
    
    func setUpTimer(numberOfSeconds: Int) {
        secondsLeftInTimer = Double(exactly: numberOfSeconds)
    
    func scheduleNotificationAfter(numberOfSeconds: Double) {
        let content = UNMutableNotificationContent()
        content.title = "Time's up!"
    
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: numberOfSeconds, repeats: false)
    
        let notification = UNNotificationRequest(identifier: "Times Up Notification", content: content, trigger: trigger)
    
        UNUserNotificationCenter.current().add(notification, withCompletionHandler: nil)
    }
    

    谢谢你的意见!学习斯威夫特一直很有趣,但它就像漂浮在黑暗的海洋上。。。不知道方向。即使是像这样简单的回答也可以肯定我的方向是正确的,至少哈哈。干杯<代码>计时器仅在您处于前台时运行。我不相信它对于这里描述的用例是非常有效的。谢谢你的回复!是的,一个缺点是网络依赖性,尽管我已经准备好无论如何只需要它。。。使世界成为管理我的数据更容易的地方。我也在使用谷歌服务器上的UTC(以防与lol相关)。非常感谢您对离线可用性的建议,我也一定会考虑的!是的,您肯定希望在UTC完成所有工作;如果你所关心的只是两点之间的时间差,那么事情就简单多了。@RobNapier我的iOS职业生涯是从读你的书开始的,iOS 6推动你的liimts,我很荣幸你同意我的解决方案:)。。对不起,我不知道还有什么其他的方式来表示感谢!(本可以使用twitter;))我还没有在我的应用程序中研究和实现通知,但这太棒了!我肯定会再回到这条路的后面。。。看起来很简单。谢谢我很高兴你喜欢它,但我想我不明白你在找什么,因为这似乎是你问题的答案。这将为您提供一个在预定义的时间段后触发的事件,该事件将完全在后台运行。你需要什么?哦,我明白了,嗯。。。我把它解读为更多的通知(比如当你在锁屏上看到小横幅时)。例如,当应用程序关闭时,将发送剩余的秒数