【问题标题】:(Swift) What API Implementation is Used for Background Timers?(Swift) 后台定时器使用什么 API 实现?
【发布时间】:2018-04-24 16:53:28
【问题描述】:

考虑像《皇室战争》(以及无数其他)这样的应用程序,它有一个奖励倒计时计时器,它会每隔一段时间生成奖励或其他游戏内货币,这是如何做到的?

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

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

非常感谢!

【问题讨论】:

    标签: swift timer background countdown


    【解决方案1】:

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

    【讨论】:

    • 感谢您的意见!学习 Swift 非常有趣,但它就像漂浮在海洋中的肮脏……不知道方向。即使是像这样简单的回答也有助于确认我至少指向了正确的方向哈哈。干杯!
    【解决方案2】:

    我同意莫希特的观点。我认为您的设计没有重大问题。有办法通过直接破解程序来打败它,但每个解决方案都是如此。您的方法简单、优雅且有效地抵御大多数简单的攻击(解决复杂的攻击要困难得多,而且可能不值得)。这绝对是我的第一选择。

    我会推荐证书固定。否则,很容易欺骗您的程序读取错误的时间服务器。证书固定告诉您的应用仅信任特定的 HTTPS 证书或证书组,而不是仅信任设备信任的每个证书。有关介绍,请参阅Certificate Pinning。有关简化它的 ObjC 实现,请参阅CertificateValidator

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

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

    【讨论】:

    • 感谢您的回复!是的,一个缺点是网络依赖性,尽管我有点倾向于只需要它......让世界成为管理我的数据的更容易的地方。我也在使用来自谷歌服务器的 UTC(如果这是相关的,哈哈)。非常感谢您对离线可用性的建议,我也一定会考虑的!
    • 是的,您肯定希望在 UTC 中完成所有工作;如果您只关心两个时间点之间的差异,它只会让事情变得更简单。
    • @RobNapier 我通过阅读您的书 iOS 6 Pushing your liimts 开始了我的 iOS 职业生涯,我很荣幸您同意我的解决方案 :) .. 抱歉,但不知道任何其他方法说声谢谢! (可以使用推特;))
    【解决方案3】:

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

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

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

    (因为其他版本使用选择器,谁想在写 Swift 时写笨拙的 Objective-C,如果他们可以避免的话?)

    所以,举个例子,假设你有

    1. 标签secondsLeftLabel 显示剩余秒数
    2. 一个函数 timesUp() 完成后你想调用它

    首先,您需要创建一个计时器属性和一个 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)
    

    【讨论】:

    • Timer 仅在您处于前台时运行。我不相信它对于这里描述的那种用例会非常有效。
    【解决方案4】:

    起初我没有意识到你的意思是真正的背景(比如当应用程序处于后台时),所以我的其他答案不起作用。对于您想要做的事情,这是计划的 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)
    }
    

    【讨论】:

    • 我还没有到在我的应用程序中研究和实现通知的地步,但这太棒了!我肯定会在更远的地方回到这个......看起来很简单。谢谢!
    • 很高兴您喜欢它,但我想我不明白您在寻找什么,因为这似乎是您问题的答案。这将为您提供一个在预定义的时间段后触发的事件,并将完全在后台运行。您缺少什么?
    • 哦,我明白了,嗯...我认为它更多地用于通知(例如当您在锁定屏幕上看到小横幅时)。例如,当应用程序关闭时,剩余的秒数被发送到“scheduleNotificationAfter(numberOfSeconds: Double)”函数,然后在该时间量之后,横幅(通知)通知用户......例如。 .. “时间到!”。我想有几个问题是:1)当那个计时器用完时,即使在后台也能触发其他函数/更新变量吗? 2) 当设备关闭时会发生什么?
    • 是的。我们通常实现一个通知委托来处理通知的接收。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-22
    • 2016-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多