About GCD (Grand Central Dispatch) in an extension.

We are currently developing a VoIP application that supports Local Push extention.

I would like to ask for your advice on how the extension works when the iPhone goes into sleep mode.

Our App are using GCD (Grand Central Dispatch) to perform periodic processing within the extension, creating a cycle by it.

[sample of an our source]

class LocalPushProvider: NEAppPushProvider {
 
    let activeQueue: DispatchQueue = DispatchQueue(label: "com.myapp.LocalPushProvider.ActiveQueue", autoreleaseFrequency: .workItem)
    var activeSchecule: Cancellable?
    override func start(completionHandler: @escaping (Error?) -> Void) {
              :
        self.activeSchecule = self.activeQueue.schedule(
            after: .init(.now() + .seconds(10)),            // start schedule after 10sec
            interval: .seconds(10)                          // interval 10sec
        ) {
            self.activeTimerProc()
        }
        completionHandler(nil)
    }
}

However In this App that we are confirming that when the iPhone goes into sleep mode, self.activeTimerProc() is not called at 10-second intervals, but is significantly delayed (approximately 30 to 180 seconds).

What factors could be causing the timer processing using GCD not to be executed at the specified interval when the iPhone is in sleep mode?

Also, please let us know if there are any implementation errors or points to note.

I apologize for bothering you during your busy schedule, but I would appreciate your response.

What factors could be causing the timer processing using GCD not to be executed at the specified interval when the iPhone is in sleep mode?

I'm not sure of exactly how this is playing out, but I suspect it's an interaction between:

  • The device going to sleep means the scheduling priority has been greatly reduced.

  • On the Scheduler side you haven't set a tolerance, which technically gives the system broad latitude when things actually occur.

  • If your goal is simply set up a repeating time, I would use DispatchSourceTimer directly instead of going through Combine.

Having said all that, the correct answer here is to use NEAppPushProvider.handleTimerEvent(), which was created to specifically do what you're trying to do.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you for your advice. I understood the usefulness of NEAppPushProvider.handleTimerEvent().

but I found the following description in "Establish and Monitor the Network Connection"

Your push provider must implement the handleTimerEvent callback method, which the framework calls every 60 seconds. Implement this method to check the status of the connection on the client end.

Is the execution interval of NEAppPushProvider.handleTimerEvent fixed at 60 seconds? If it can be set to an arbitrary interval, would you tell me sample source or how to configure it.

SO, let me actually jump back to what you said here:

However In this App that we are confirming that when the iPhone goes into sleep mode, self.activeTimerProc() is not called at 10-second intervals,

What you're trying to do here is exactly the kind of behavior push provider is trying to discourage. That is, on a well designed network, there's no reason why your push provider couldn't:

  • Open a connection to the server and perform initial setup.

  • Leave the connection open without sending or receiving any data.

  • An arbitrarily long period of time later, the server sends a call notification and the actual call process starts.

Now, many voip apps do not actually work that way. In my experience that, that's generally caused by one of both of these two factors:

  1. The server implementation is being treated as a fixed constraint which cannot be modified and that (generally, VERY old) implementation include requirements which directly prevent the approach above.

  2. The underlying WiFi network infrastructure is very poor/broken.

...and in both cases the solution developer seem to gravitate toward is to increase the app activity in an attempt to mask these issues. Note that, in my experience, that approach is only marginally beneficial. It will reduce the "problem", but it rarely reduces it to the point where the app actually works well.

In practice, that means it ends up converting a more visible failure that could have been fully resolved with broader architectural or into a mess of bugs/glitches which are far more difficult to investigate and understand. The problem is that those remaining issues cannot actually be fixed on the app side, as that is not in fact what's causing the problem. Indeed, over the years I've worked with multiple voip apps where the developer was STILL having these kind of problem despite that fact that they'd (often unknowingly) used the "audio" background category to keep their app awake in the background at ALL times.

That leads to here:

Thank you for your advice. I understood the usefulness of NEAppPushProvider.handleTimerEvent(). but I found the following description in "Establish and Monitor the Network Connection" Your push provider must implement the handleTimerEvent callback method, which the framework calls every 60 seconds. Implement this method to check the status of the connection on the client end. Is the execution interval of NEAppPushProvider.handleTimerEvent fixed at 60 seconds?

Yes and, no, it cannot be changed. If your design requires your provider to be generating it's own traffic* more frequently than that, then you need to fix your design.

*Keep in mind that this is NOT about how frequently your push provider is woken up or allowed to handle network traffic. Nothing is specifically constraining your providers interactions with your server, so you're server could actually pinging your push provider at whatever frequency it wanted. The issue here is that you want the app to generate the traffic which the server responds to, instead of having the server generate the traffic the app responds to.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

About GCD (Grand Central Dispatch) in an extension.
 
 
Q