如何在swift 3中为iphone制作一个重复的倒数计时器应用程序,即使在后台也会每隔'x'分钟发出一次通知

时间:2022-02-10 05:18:11

I'm trying to make a simple app that fires off an alarm every 7 minutes (configurable of course) whether the app is in the foreground or not.

我正在尝试创建一个简单的应用程序,每7分钟发出一次警报(当然可以配置),无论应用程序是否在前台。

Although it was easy enough to just make a hardcoded repeating timer that does this, adding any kind of front end interface seems to throw a wrench into my strategy, suggesting to me that maybe I am taking the wrong approach entirely.

虽然只做一个硬编码的重复计时器很容易做到这一点,添加任何类型的前端界面似乎都会对我的策略产生影响,向我暗示我可能完全采取了错误的方法。

For example, if I just have a start / stop button, pausing an existing timer needs to cancel the background notification, but keep track of how much time is left. Then when it is resumed, a notification needs to be recreated with the remaining time. For example, if it is a 5 minute timer, that is paused with only 2 minutes left, when it is resumed, it is set up to be a 2 minute countdown timer. So if I put the app in the background, I will get a notification that the time has elapsed, but it won't automatically start a 5 minute countdown, it will instead just go back to 2 minutes. I need to be able to create notifications that are repeating, and yet switch to the full duration once the "remaining" time has expired.

例如,如果我只有一个开始/停止按钮,暂停现有的计时器需要取消后台通知,但要记录剩余的时间。然后,当它恢复时,需要使用剩余时间重新创建通知。例如,如果它是一个5分钟的计时器,只剩下2分钟暂停,当它恢复时,它被设置为2分钟的倒数计时器。因此,如果我将应用程序置于后台,我会收到时间已过的通知,但它不会自动开始5分钟的倒计时,而是会回到2分钟。我需要能够创建重复的通知,并在“剩余”时间到期后切换到完整的持续时间。

I could create an action on the notification, which if the user pressed would restart the timer, but for this purpose, I need the timer to automatically restart immediately, even if the user ignores it.

我可以在通知上创建一个动作,如果用户按下它将重新启动计时器,但为此目的,我需要计时器立即自动重启,即使用户忽略它。

I could easily accomplish this with foreground timers, but then I would need to force my phone to never go to sleep, and it would only work while this app was in the foreground, which is not always possible on a phone.

我可以很容易地用前景定时器实现这一点,但后来我需要强制我的手机永远不会进入睡眠状态,只有当这个应用程序处于前台时才会工作,这在手机上并不总是可行的。

Other timer apps can pop up background notifications just fine. And the calendar app can schedule background notifications for arbitrary times. I doubt the calendar app schedules all future (including repeating) alerts the moment it is started, since it works even if the phone was restarted and the calendar app never started. Therefore the calendar app notification mechanism must be smart enough to fire off an alarm, and then schedule the next alarm, which is exactly the kind of mechanism I need here. I thought maybe the calendar app just uses server-based remote notifications, and the server handled the more complex logic needed, but that can't be true since the calendar app notifications work fine even without any internet connection.

其他计时器应用程序可以正常弹出背景通知。日历应用程序可以安排任意时间的背景通知。我怀疑日历应用程序会在启动时安排所有未来(包括重复)警报,因为即使手机重新启动且日历应用程序从未启动,它也能正常工作。因此,日历应用程序通知机制必须足够智能以触发警报,然后安排下一个警报,这正是我需要的机制。我想也许日历应用程序只使用基于服务器的远程通知,服务器处理所需的更复杂的逻辑,但这不可能是真的,因为即使没有任何互联网连接,日历应用程序通知也能正常工作。

I've done a fair bit of research on this, but can't seem to find what to do. If I could pass in a tiny bit of code to be executed when the notification is triggered, that would work, but I see no way of doing that.

我对此做了一些研究,但似乎无法找到该做什么。如果我可以在触发通知时传入一小段代码来执行,那就行了,但我认为没办法这样做。

This is what my notification creation code looks like at the moment:

这就是我的通知创建代码目前的样子:

    let content = UNMutableNotificationContent()

    content.title = "Context switch"
    content.body = "\(Int(initialDuration / 60)) minutes have passed."
    content.sound = UNNotificationSound.default()
    content.categoryIdentifier = generalCatId

    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: remaining, repeats: true)
    let request = UNNotificationRequest(identifier: requestIdentifier, content: content, trigger: trigger)

    let center = UNUserNotificationCenter.current()
    center.delegate = self

    center.add(request) { (error) in
        if error != nil {
            print(error!)
        }
    }

1 个解决方案

#1


1  

the five basic background modes available in iOS:

iOS中提供的五种基本背景模式:

  1. Play audio the app can continue playing and/or recording audio in the background.
  2. 播放音频,应用程序可以继续播放和/或在后台录制音频。

  3. Receive location updates the app can continue to get callbacks as the device’s location changes.
  4. 接收位置更新,应用可以随着设备位置的变化继续获得回调。

  5. Perform finite-length tasks the generic “whatever” case, where the app can run arbitrary code for a limited amount of time.
  6. 执行有限长任务的通用“无论”情况,应用程序可以在有限的时间内运行任意代码。

  7. Process Newsstand Kit downloads specific to Newsstand apps, the app can download content in the background.
  8. 针对报亭应用程序特定的Process Newsstand Kit下载,该应用程序可以在后台下载内容。

  9. Provide Voice-over-IP (VoIP) services the app can run any arbitrary code in the background. Of course, Apple limits its use so that your app must provide VoIP service, too.
  10. 提供IP语音(VoIP)服务,应用程序可以在后台运行任意代码。当然,Apple限制其使用,以便您的应用程序也必须提供VoIP服务。

Ref

Sorry, you can run the timer in the background forever so you have to think to do something doesn't depend on Perform finite-length tasks background mode.

对不起,你可以永远在后台运行计时器,所以你必须考虑做一些不依赖于执行有限长度任务的后台模式。

So you have 3 options:

所以你有3个选择:

  1. register your app to receive location updates and this need Requesting Permission to Use Location Services requestAlwaysAuthorization()

    注册您的应用程序以接收位置更新,这需要请求使用位置服务的权限requestAlwaysAuthorization()

  2. check the device time every time you go to foreground and this will not work correctly if user changes the time manually so best thing to do is calling web service to get current time in UTC

    每次前往前台检查设备时间,如果用户手动更改时间,这将无法正常工作,因此最好的办法是调用Web服务以获取UTC时间

    http://www.timeapi.org/utc/now

  3. register to local notification when your app sends to background

    当您的应用发送到后台时注册到本地通知

        var localNotification = UILocalNotification()
        localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
        localNotification.alertBody = "Time out"
        localNotification.timeZone = NSTimeZone.defaultTimeZone()
        localNotification.applicationIconBadgeNumber = UIApplication.sharedApplication().applicationIconBadgeNumber + 1
    

then schedule this notification UIApplication.sharedApplication().scheduleLocalNotification(localNotification)

然后安排此通知UIApplication.sharedApplication()。scheduleLocalNotification(localNotification)

#1


1  

the five basic background modes available in iOS:

iOS中提供的五种基本背景模式:

  1. Play audio the app can continue playing and/or recording audio in the background.
  2. 播放音频,应用程序可以继续播放和/或在后台录制音频。

  3. Receive location updates the app can continue to get callbacks as the device’s location changes.
  4. 接收位置更新,应用可以随着设备位置的变化继续获得回调。

  5. Perform finite-length tasks the generic “whatever” case, where the app can run arbitrary code for a limited amount of time.
  6. 执行有限长任务的通用“无论”情况,应用程序可以在有限的时间内运行任意代码。

  7. Process Newsstand Kit downloads specific to Newsstand apps, the app can download content in the background.
  8. 针对报亭应用程序特定的Process Newsstand Kit下载,该应用程序可以在后台下载内容。

  9. Provide Voice-over-IP (VoIP) services the app can run any arbitrary code in the background. Of course, Apple limits its use so that your app must provide VoIP service, too.
  10. 提供IP语音(VoIP)服务,应用程序可以在后台运行任意代码。当然,Apple限制其使用,以便您的应用程序也必须提供VoIP服务。

Ref

Sorry, you can run the timer in the background forever so you have to think to do something doesn't depend on Perform finite-length tasks background mode.

对不起,你可以永远在后台运行计时器,所以你必须考虑做一些不依赖于执行有限长度任务的后台模式。

So you have 3 options:

所以你有3个选择:

  1. register your app to receive location updates and this need Requesting Permission to Use Location Services requestAlwaysAuthorization()

    注册您的应用程序以接收位置更新,这需要请求使用位置服务的权限requestAlwaysAuthorization()

  2. check the device time every time you go to foreground and this will not work correctly if user changes the time manually so best thing to do is calling web service to get current time in UTC

    每次前往前台检查设备时间,如果用户手动更改时间,这将无法正常工作,因此最好的办法是调用Web服务以获取UTC时间

    http://www.timeapi.org/utc/now

  3. register to local notification when your app sends to background

    当您的应用发送到后台时注册到本地通知

        var localNotification = UILocalNotification()
        localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
        localNotification.alertBody = "Time out"
        localNotification.timeZone = NSTimeZone.defaultTimeZone()
        localNotification.applicationIconBadgeNumber = UIApplication.sharedApplication().applicationIconBadgeNumber + 1
    

then schedule this notification UIApplication.sharedApplication().scheduleLocalNotification(localNotification)

然后安排此通知UIApplication.sharedApplication()。scheduleLocalNotification(localNotification)