0

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.

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.

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!)
        }
    }
Jeremy Gurr
  • 1,613
  • 8
  • 11
  • Have you taken a look at Apple's Local and Remote Notification Programming Guide? https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/index.html – Ryan H. Nov 23 '16 at 23:32
  • Yeah I couldn't find a way to execute arbitrary code triggered by the timer when the app is in the background. Is what I'm trying to do impossible? It seems like a sensible use case to me. – Jeremy Gurr Nov 24 '16 at 01:21

1 Answers1

1

the five basic background modes available in iOS:

  1. Play audio the app can continue playing and/or recording audio in the background.
  2. Receive location updates the app can continue to get callbacks as the device’s location changes.
  3. Perform finite-length tasks the generic “whatever” case, where the app can run arbitrary code for a limited amount of time.
  4. Process Newsstand Kit downloads specific to Newsstand apps, the app can download content in the background.
  5. 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.

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:

  1. register your app to receive location updates and this need Requesting Permission to Use Location Services 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

    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)

Henawey
  • 508
  • 1
  • 9
  • 18
  • The location strategy probably wouldn't work since I'm usually not moving when I need the repeating timers to be triggerred. Checking when it's only in the foreground also makes the app worthless, since my phone might go to sleep and I may forget to check it, and then I won't be notified when the event happens. Registering a local notification works fine for the first time the timer expires, but what about all the times after that? The repeating option can't reset the timer to the original duration. – Jeremy Gurr Nov 24 '16 at 01:24
  • Yes, you are right about location strategy so you have to eliminate this option, I don't get what you mean about local notification. iOS by default didn't make your application run in the background for a long time and it depends on OS you can't do anything. so you work must execute in a foreground, no way to execute your code in a background except the above states. your application will be useless if you expect something like service in Android. you can register more than one timer with Local Notification. – Henawey Nov 24 '16 at 05:22
  • Also, you can change the Sound of Local notification, you can create widget and this should make your app work without need for foreground state for your app but the widget will update the data if the widget itself is visible for the user – Henawey Nov 24 '16 at 05:29
  • No I just expect to be able to do the same kind of things that other timer apps do. The calendar app can schedule arbitrary notifications, and it works whether or not the calendar app has ever been started, and it works without an internet connection. Maybe the calendar app is a hard coded special case. The clock app works similarly, so it either also uses a special case notification mechanism only available to it and the calendar, or there is a public api that can be used to accomplish the same, which seems likely considering so many similar apps out there. – Jeremy Gurr Nov 25 '16 at 15:29
  • I find a workarounds to achieve this but remember those are workaround. http://stackoverflow.com/questions/22628922/how-to-run-nstimer-in-background-beyond-180sec-in-ios-7 – Henawey Nov 26 '16 at 08:22
  • Unless I'm missing something, that post seems to say what has already been said: that you have to use one of the background modes like location services or playing background audio. Another question is linked to there which suggests a hack that stops and starts location services to keep a timer going, but not only is that a weak hack, but it certainly wouldn't be accepted by the app store. There's got to be a legitimate way to do this. – Jeremy Gurr Nov 26 '16 at 16:48
  • For now I don't know and I don't think this will be available soon as Apple depends on CPU and RAM usages optimization to reach such performance in iDevices so only foreground app and authorized background tasks with background mode enable apps are running and otherwise will be suspended (it's not real multitasking), You can check iOS app states for more information – Henawey Nov 27 '16 at 11:36