Notifications on iOS sourced from a machine on an offline local network

We have a device which is an appliance and we are developing a control interface app for macOS and iOS/iPadOS.

How can we set up our iOS application to grab information from a local network device while it is in the background in order to show notifications?

Communication between the Apple device and our device is via local networking and the device is designed to be used on networks without internet connections. On networks with internet connections we could forward events from the device, via a server and APNS push notifications, but that isn't valid here.

Events occur on our device and are forwarded to clients, who are subscribed to Server-Sent Events. On macOS this works well and the application can receive updates and show Notification Center notifications fine.

On iOS we are using a BGAppRefreshTaskRequest with time interval set to 1 minute, but it appears that we get scheduled only every few hours. This isn't very useful as notifications just arrive in batches rather than in a timely manner. All normal networking is closed when the app goes into the background, so we cannot keep the SSE request open.

Another idea which we haven't tried yet: Creating a new endpoint on the device which keeps the connection open until a notification arrives, then using background URLSession to poll on that endpoint. Would that work? It seems like a mis-use of the API perhaps?

Answered by DTS Engineer in 816859022

At this point I’m going to ask a colleague to wade into this discussion.

That would be me...

First off, a quick clarification here:

On iOS we are using a BGAppRefreshTaskRequest with time interval set to 1 minute, but it appears that we get scheduled only every few hours. This isn't very useful as notifications just arrive in batches rather than in a timely manner.

BGAppRefreshTask scheduling is HEAVILY driven by the app usage pattern of a particular device, which means it's behavior can have a VERY wide swing. A pretty good way to thing about it is that it's theoretical "goal" is to have your task "immediately before" the user opens your app to do whatever they want to do. For a heavily used app, it can run "a few time an hour", but "every few hours" is inline with what I'd expect for an app that the user isn't interacting with "all the time".

My big warning here is that on dedicated development devices, these heuristics can trick you into thinking it will fire FAR more frequently that it will in reworld use. The combination always being charged (because you leave the device plugged in) and VERY restricted app usage (because the "only" app you ever run is "your app") can end up WILDLY skewing the heuristic in favor of your app in a way that's completely unrealistic under real world conditions.

Similarly:

Another idea which we haven't tried yet: Creating a new endpoint on the device which keeps the connection open until a notification arrives, then using background URLSession to poll on that endpoint. Would that work?

No, I don't think this will really work. While the background URL session can and will wake your app in the background, it was really designed for "bulk" background transfers, not for any kind of "real time" networking. It's scheduling is controlled by the same heuristic ("duet") system that schedules refresh task, except the scheduling "goal" is basically "transfer the data when it won't disrupt the user and/or the network bandwidth would be minimally disruptive". It's hard to predict how exactly it would behave in your case, but the BEST I'd expect is worse that BGAppRefreshTask (less frequent and less predictable). However, I wouldn't be surprised if it simply deferred your entire download until the overnight charging window, making the entre approach useless.

As a quick comment here:

It seems like a mis-use of the API perhaps?

One thing to understand about your background API design is that overtime we've become more and more intentional about designing APIs that simply can't be abused/misused. The background task framework and background URL session are perfect examples of this, as both of them are specifically designed to throttle themselves in ways that basically make abusing them impossible. At this point, if you think you've found an API "trick" that will let your app stay awake in the background "forever", it's more likely that you've overlooked some detail of it's implementation that will specifically prevent/restrict that from happening.

Returning to your question:

How can we set up our iOS application to grab information from a local network device while it is in the background in order to show notifications?

This is an issue that we don't provide any general purpose solution for, but the closest API solution would be a Local push connectivity extension. That API allows an app to register the WiFi network it wants to run on and the system will then run the extension anytime the device is connected to one of those network. The extension then connects to its target server and notifies the user of messages or incoming calls the server tells it about.

However, I don't think you'll be able to use that API. That API was created to support VOIP apps that were specifically designed built for use case were PushKit simply could not function ("Cruise Ships" are the standard example) and entitlement approval is generally restricted to those use cases. You can apply for the entitlement (particularly if you'd like to provide more details about your product and use case), but I want to be clear that approval is not guaranteed or even likely.

As for other options... I don't really have any, at least not given the information you've provided. iOS's basic background execution design is that app should only run in the background for limited amounts of time in order to complete specific tasks/jobs, which is exactly the opposite of what you want. Lots of apps do background networking, but there is always some kind external use case/action that enables that network activity. There isn't any API that allows the kind of unbounded networking you'd need.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

the device is designed to be used on networks without internet connections.

Can you give me a bit more background about what this device is and what sort of networks it might find itself on?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

It is a data processing device for media files. It is designed to be used on location or on set for video productions. In these locations there may or may not be internet. Examples:

  • iPad Pro, USB ethernet directly connected
  • Macbook, using TBT4 networking
  • iPhone, using Wi-Fi network served from our device

Networking allows control of the device and serving of files over SMB which can be mounted on the apple device

And how do notifications fit into this? Would it, for example, display a notification when it’s finished processing an asset?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Yeah exactly that, a task finishes on the device, which we want to tell the user about, even if the application is off-screen at the time, so we want them in the Notification Center.

Thanks for confirming.

At this point I’m going to ask a colleague to wade into this discussion. Please stand by.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

At this point I’m going to ask a colleague to wade into this discussion.

That would be me...

First off, a quick clarification here:

On iOS we are using a BGAppRefreshTaskRequest with time interval set to 1 minute, but it appears that we get scheduled only every few hours. This isn't very useful as notifications just arrive in batches rather than in a timely manner.

BGAppRefreshTask scheduling is HEAVILY driven by the app usage pattern of a particular device, which means it's behavior can have a VERY wide swing. A pretty good way to thing about it is that it's theoretical "goal" is to have your task "immediately before" the user opens your app to do whatever they want to do. For a heavily used app, it can run "a few time an hour", but "every few hours" is inline with what I'd expect for an app that the user isn't interacting with "all the time".

My big warning here is that on dedicated development devices, these heuristics can trick you into thinking it will fire FAR more frequently that it will in reworld use. The combination always being charged (because you leave the device plugged in) and VERY restricted app usage (because the "only" app you ever run is "your app") can end up WILDLY skewing the heuristic in favor of your app in a way that's completely unrealistic under real world conditions.

Similarly:

Another idea which we haven't tried yet: Creating a new endpoint on the device which keeps the connection open until a notification arrives, then using background URLSession to poll on that endpoint. Would that work?

No, I don't think this will really work. While the background URL session can and will wake your app in the background, it was really designed for "bulk" background transfers, not for any kind of "real time" networking. It's scheduling is controlled by the same heuristic ("duet") system that schedules refresh task, except the scheduling "goal" is basically "transfer the data when it won't disrupt the user and/or the network bandwidth would be minimally disruptive". It's hard to predict how exactly it would behave in your case, but the BEST I'd expect is worse that BGAppRefreshTask (less frequent and less predictable). However, I wouldn't be surprised if it simply deferred your entire download until the overnight charging window, making the entre approach useless.

As a quick comment here:

It seems like a mis-use of the API perhaps?

One thing to understand about your background API design is that overtime we've become more and more intentional about designing APIs that simply can't be abused/misused. The background task framework and background URL session are perfect examples of this, as both of them are specifically designed to throttle themselves in ways that basically make abusing them impossible. At this point, if you think you've found an API "trick" that will let your app stay awake in the background "forever", it's more likely that you've overlooked some detail of it's implementation that will specifically prevent/restrict that from happening.

Returning to your question:

How can we set up our iOS application to grab information from a local network device while it is in the background in order to show notifications?

This is an issue that we don't provide any general purpose solution for, but the closest API solution would be a Local push connectivity extension. That API allows an app to register the WiFi network it wants to run on and the system will then run the extension anytime the device is connected to one of those network. The extension then connects to its target server and notifies the user of messages or incoming calls the server tells it about.

However, I don't think you'll be able to use that API. That API was created to support VOIP apps that were specifically designed built for use case were PushKit simply could not function ("Cruise Ships" are the standard example) and entitlement approval is generally restricted to those use cases. You can apply for the entitlement (particularly if you'd like to provide more details about your product and use case), but I want to be clear that approval is not guaranteed or even likely.

As for other options... I don't really have any, at least not given the information you've provided. iOS's basic background execution design is that app should only run in the background for limited amounts of time in order to complete specific tasks/jobs, which is exactly the opposite of what you want. Lots of apps do background networking, but there is always some kind external use case/action that enables that network activity. There isn't any API that allows the kind of unbounded networking you'd need.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks for the link to the Local Push extension stuff. I had looked at that before and forgotten what it was called.

It still doesn't solve USB / Thunderbolt / Wired networking as there is no way to specify those networks so might not be the right direction for us.

It still doesn't solve USB / Thunderbolt / Wired networking as there is no way to specify those networks so might not be the right direction for us.

Please file a bug on this as soon as possible and post the bug number once it's filed. This is an interesting edge case (hard wired networks) could be worth addressing but we haven't actually gotten that many requests for it. As part of that bug, please include details and background on your product and how you'd like to use this support.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

I have filed a feature request FB16077739

My suggestion is that Local push could match on a DNS-SD advertisment provided by the device. That way it could work on all types of local network.

Notifications on iOS sourced from a machine on an offline local network
 
 
Q