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?
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