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

时间:2018-04-24 16:53:29

标签: swift timer background countdown

考虑像Clash Royale(以及无数其他人)这样的应用程序,它有一个奖励倒数计时器,可以经常产生奖励或其他游戏内货币,这是怎么做到的?

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

还有其他东西可以处理这种背景倒计时吗?

非常感谢!

4 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

我同意Mohit。我发现你的设计没有重大问题。有办法通过直接攻击程序来打败它,但每个解决方案都是如此。你的简单,优雅,有效对抗大多数简单的攻击(解决复杂的攻击很难很多,可能不值得)。这肯定是我的第一选择。

我建议使用证书固定。否则,让你的程序读取错误的时间服务器有点容易。证书锁定会告诉您的应用只信任特定的HTTPS证书或证书组,而不仅仅是信任设备所信任的每个证书。有关简介,请参阅Certificate Pinning。有关简化它的ObjC实现,请参阅CertificateValidator

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

如果需要离线访问,您可以使用的工具是mach_absolute_time。这使用起来比较复杂,并且比您的方法更难以保护,但它可以脱机工作。 mach_absolute_time返回单调递增的时钟。也就是说,即使用户修改系统时钟,它也总是以恒定速率上升。然而,它不是几秒钟。您需要使用AbsoluteToDuration进行转换。 (有关详细信息,请参阅QA1398。)它会在设备重新启动时重置。原则上,您可以将它提供的时间与系统时钟提供的时间进行比较,以确定系统时钟是否值得信任,如果是,您可以在离线时奖励积分。这是否值得额外的工作(它仍然可以有假阴性)取决于你的用例。

答案 2 :(得分:0)

Foundation库包含一个Timer类,您可以阅读here的文档。

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

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

(因为其他版本使用选择器,并且当他们正在编写Swift时,谁想要编写kludgy Objective-C,如果他们可以避免它?)

所以,举个例子,让我们说你有

  1. 显示剩余秒数的标签secondsLeftLabel
  2. 完成后要调用的函数timesUp()
  3. 首先,您需要创建一个timer属性和一个secondsLeftInTimer属性:

    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)
    

答案 3 :(得分:0)

我一开始并没有意识到你的意思是真正的背景(就像应用程序背景一样),所以我的另一个答案不会起作用。对于您想要做的事情,这是计划的UNNotification的正确用例。

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

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)
}