5

I am using a CADisplayLink as a timer in my application. I am using a CADisplayLink because my app relies HEAVILY on the accuracy/precision of the CADisplayLink. NSTimer is not a suitable replacement in my case.

The issue is that occasionally, I need the CADisplayLink to fire and call its selector, while the application is in the background. Is this even possible? I understand that the CADisplayLink is linked to the refresh rate of the application UI. Does this mean that, when my app is in the background, there is no UI, and thus it is impossible to use a CADisplayLink in this case? Or, is it possible to somehow use the CADisplayLink by changing the run loop that I add it to or by changing the forMode argument? or perhaps using some GCD trickery/work-around?

Note: My app is using an AudioPlayer and thus I have background capabilities enabled. I am able to run other code while in the background. Just to test, I switched my CADisplayLink with an NSTimer, and the NSTimer fired and called its selector as I wanted it to. I just can't get the CADisplayLink to fire and I can't find any definitive documentation regarding the possibility of using it in the background

Here is my current code pertaining to my display link:

func beginTimer(){

  dispatch_async(dispatch_get_main_queue()){
      self.displayLink = CADisplayLink(target: self, selector: "update")
      self.displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
  }
}

func update(){
  delegate?.executeCallback
 }
MikeG
  • 3,745
  • 1
  • 29
  • 51
  • 1
    I was going to chide you for not experimenting to learn the answer to your own question. Then I read your discussion more deeply and decided that not only have to experimented, you've experimented definitively. You've surely answered your own question! – matt Mar 14 '16 at 00:54
  • Thanks @matt , I guess you are right. I was just hoping I left some stone unturned. Do you know of any timer more/equally as accurate as the CADisplayLink? I do not require a high level of discretion between intervals (ie: 0.5 second intervals acceptable), I "only" require a very consistent, or accurate, timer. One that I can guarantee will fire every 0.5 seconds, on the 1/2 second mark every time. Does this exist in iOS? – MikeG Mar 14 '16 at 21:21
  • 1
    "Does this exist in iOS" No, not really. Not even CADisplayLink is totally regular, after all. Besides, consider this: what if it fires and the main thread at that moment is busy? In short, if you're not willing to accept a certain degree of jitter, you'll be a very unhappy camper no matter what you use. – matt Mar 14 '16 at 21:31

1 Answers1

2

It makes an undocumented sort of sense that your display link callback is not called in the background, seeing as you shouldn't be drawing anyway (in fact if you do any GPU work in the background your app gets killed).

So if you're already using audio and a background mode why not try implementing your timing by doing your audio with an remoteIO audio unit* and counting output samples instead of screen refreshes?

With this path you can choose an output buffer size equivalent to or smaller than the usual 60Hz screen refresh.

I think you ought to be able get comparable (and maybe better) timing accuracy, although there will be caveats in low power mode / other app interactions where iOS may choose to change your buffer size from underneath you. Some experimentation will be necessary.

*I don't think AVAudioEngine has an equivalent of this procedural output yet.

Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
  • Thanks for this insight, I will have to look into it. Is there any way of doing this in iOS that you are aware of? I am unable to find any word on it unfortunatley. – MikeG Mar 14 '16 at 21:14
  • With AVAudioEngine, you can use the installTap function to achieve this.(https://developer.apple.com/documentation/avfoundation/avaudionode/1387122-installtap) – Taiwosam Jan 10 '19 at 11:43