0

I am developing a Cocoapod library in which, i have to Call a function when timer fires. I am using that Cocoapod in an iOS App, Timer is not firing when App goes to background state

private var timer : DispatchSourceTimer? = nil

let queue = DispatchQueue(label: "sample.timer")
timer = DispatchSource.makeTimerSource(queue : queue)
timer?.setEventHandler 
{
    [weak self] in
    self?.sendData()
}
timer?.scheduleRepeating(deadline: .now(), interval: .seconds(5))
Rob
  • 415,655
  • 72
  • 787
  • 1,044
Gokul
  • 97
  • 1
  • 11
  • 1
    And what's your question? – Quoc Nguyen Mar 28 '18 at 05:44
  • How to fire the timer even in the background mode@QuocNguyen – Gokul Mar 28 '18 at 07:19
  • I know you want to keep an app alive running a timer every five seconds, but (a) it's a bad idea because it will kill the user's battery; and (b) it's in violation of app store guidelines. If you tell us what the functional purpose of this timer is, we might be able to advise you on how to best achieve what you're trying to do. Apple provides various mechanisms for a wide variety of the real-world problems. But we can't advise you further without knowing why your library wants to send data when it's in the background. – Rob Mar 28 '18 at 18:00
  • @Gokul Did you ever figure out how to run code when the timer fires while the app is in the background? I am trying to figure out the same thing. – daniel Nov 28 '18 at 13:53

2 Answers2

4

The fact that you are using a GCD timer (an alternative to Timer that can run on background threads) makes me wonder if you are conflating two completely different uses of the word “background”:

  • One meaning of “background” is related to background queues or threads while an app is running. This is in contrast to the “main” thread/queue.

    And, yes, GCD timers are different than Timer instances insofar as they can run on background queues (assuming the app, itself, is actually running).

  • The other meaning of “background” is related to the state of an application. You can either be running in the foreground, running in the background, or be suspended/terminated.

    While an app is running (either in the foreground or the background), timers (either GCD timers or standard Timer) can operate. But if an app is suspended or terminated, all execution will stop, including timers.

The problem is that you’re trying to run a timer while the app, itself, has been suspended. That will not work.

Your app will not run in the background unless you either:

  • request a little time to finish some finite length task (which is only runs for 30 seconds in recent iOS versions); or

  • your app has some background mode enabled for some approved purpose (e.g. navigation, music, VOIP, etc.).

But apps can’t continue to run in the background (timers or anything else), except for those special, narrow situations (e.g. VOIP, music, navigation apps, etc.). For more information about background execution see:

And, even in iOS 13 and later, where more generalized background tasks are permitted, they’re intended for finite length tasks that will be initiated at the sole discretion of the OS (determined by power, wifi, frequency with which the user fires up the app, etc.). For more information, see WWDC 2019 Advances in App Background Execution.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • You're missing one app state. – jnblanchard Apr 09 '18 at 20:01
  • 1
    My intent was not to walk him through the full [App Life Cycle](https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html#//apple_ref/doc/uid/TP40007072-CH2-SW1), but rather just disabuse him of the notion that the app (much less timers) just automatically keeps running when the the user leaves the app. And, frankly, if he really has questions about various alternatives for periodically posting updates, he really should clarify his question. – Rob Apr 09 '18 at 20:23
-2

It's a little hacky, but you can try to limit your time in the background and workaround this problem.

import AVFoundation

do {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, with: .mixWithOthers)
    try AVAudioSession.sharedInstance().setActive(true)
}
catch { print(error) }
jnblanchard
  • 1,182
  • 12
  • 12
  • It should be noted that this is in violation of the App Store guidelines, and if Apple notices that you do this, they are likely to reject your application. – Rob Mar 28 '18 at 17:25
  • This will not get you rejected from the store – jnblanchard Mar 28 '18 at 17:27
  • The [guidelines](https://developer.apple.com/app-store/review/guidelines/) are very clear: "Multitasking apps may only use background services for their intended purposes: VoIP, audio playback, location, task completion, local notifications, etc. If your app uses location background mode, include a reminder that doing so may dramatically decrease battery life." You can try to sneak it past them if you want, but they _will_ reject apps if they discover surreptitious use of background execution. I certainly wouldn't advise doing it for a CocoaPod for third party developers. – Rob Mar 28 '18 at 17:34
  • Using the shared instance on AVAudioSession solves this, and will never get you rejected from the store – jnblanchard Mar 28 '18 at 17:39
  • "will _never_ get you rejected" ... That's simply not true. Apps doing this certainly have slipped through the review process, but Apple has been unambiguous on this point and certainly can reject apps that do this. Now, if it is really a music/video app, that's a different matter. But if it's simply a technique to keep an app alive, they can reject it for that. Besides, it's just a bad idea. It will kill a user's battery. There's a reason that Apple's guidelines forbid this practice. – Rob Mar 28 '18 at 17:45
  • Shared instance is a system object that conforms to dispatch once protocol. As such it’s a very good way of lengthening your apps’ time in the foreground. It will never get your app rejected from the store. – jnblanchard Mar 28 '18 at 18:10
  • @jnblanchard I don't understand. How does this allow code to run when a timer fires while the app is in the background? – daniel Nov 28 '18 at 13:55
  • If your app is launched, then creates a repeating timer. That timer will not execute its block if the user open another app between executions. A way around that is to fake that you have an active audio session. This will likely trick the device into giving your app a state of background or inactive and consequently fire your repeating block after the user exits active. – jnblanchard Dec 03 '18 at 17:20