Development environment: Xcode 26.4, macOS 26.3.1 Run-time configuration: iOS 18.7.6 and higher
We have an application running on supervised devices, with an MDM profile typically deployed via jamf. The profile enables a Content Filter, with the two flags "Socket Filter" and "Browser Filter" set to true. On the device side, we implement the content filter as a network extension via:
- a class FilterDataProvider extending NEFilterDataProvider,
- a class FilterControlProvider extending NEFilerControlProvider.
For the record, the FilterDataProvider overrides the handle*() methods to allow all traffic; the handleNewFlow() simply reports the new connection to FilterControlProvider for analysis.
Problem: some customers reported that after a reboot of their device, they would not get access to the internet for up to 60s/90s. We have not been able to reproduce the problem on our own devices. What we see is that, even with our app uninstalled, without any Content Filter, it takes roughly 20s to 25s for a device to have internet access, so we can probably consider this 20s delay as a baseline.
But would you be aware of a reason that would explain the delay observed by these customers?
More details: We have conducted some tests on our devices, with extended logging. In particular:
- we have added an internet probe in the app that is triggered when the app starts up: it will try to connect to apple.com every 2s and report success or failure,
- we also have a network monitor (nw_path_monitor_set_update_handler) that reacts to network stack status updates and logs the said status.
A typical boot up sequence shows the following:
- the boot time is 7:59:05,
- the app starts up at 7:59:30 (manually launched when the device is ready),
- the probe fails and keeps failing,
- the content filter is initialized/started up 7:59:53 and is ready at 7:59:55,
- the network monitor shows that the network stack is connected (status = nw_path_status_satisfied) right after that,
- and the probe succeeds in connecting 2s later.
In other words, internet is available about 50s after boot time, 25s after app startup (i.e. after the device is actually ready). For some customers, this 25s delay can go up to 60/90s.
Thanks for bringing this to the forums.
But would you be aware of a reason that would explain the delay observed by these customers?
No. And the fact that this only happens at specific sites makes it hard to offer useful insight. What I can do is suggest a path for investigating this.
IMPORTANT I’m gonna assume you’re familiar with the system log. If not, read Your Friend the System Log. I’m also going to suggest an approach that’s kinda like the one I outlined in Using a Sysdiagnose Log to Debug a Hard-to-Reproduce Problem, so you might want to read that for more background.
The canonical tool for investigating issues like this is a sysdiagnose log. In a case like this that’s only going to work if you have a customer who is reasonably technically savvy and willing to help you debug.
A further complication is that NE content filter data providers can’t log to the system log when signed for distribution. They can only add stuff to the system log when development signed. So, to see what’s going on inside them you need to:
- Get a test device UDID from your customer.
- Add that to your team.
- Use the Xcode organiser to export a development-side version of your production app.
- Have the customer install that on their test device.
They’ll then be able to capture a sysdiagnose log that includes the log entries from your content filter data provider.
At that point I recommend that you add some log points to each of your providers:
- Add a ‘first light’ log point, as explained in Debugging a Network Extension Provider.
- Add a log point at the start of your
startFilter(…)method. - And another just before you call the completion handler.
- Finally, add one to
handleNewFlow(…)method. If you’re worried about overwhelming the log, you can limit this to just the first time that thehandleNewFlow(…)method is called.
These log entries should be persistent in the system log and allow you to establish a more accurate timeline.
With all of the above in place, you should be able to run a test like so:
- Identify an affected customer who’s willing and able to help.
- Send them a development-signed version of your app, as described above.
- Have them use that to reproduce the problem.
- And that capture a sysdiagnose log short thereafter.
Next, have them install the VPN (Network Extension) for iOS/iPadOS debug profile from our Bug Reporting > Profiles and Logs page and repeat the process. This will give you a second sysdiagnose log with extra NE debugging.
Oh, and before you do this, run through this process yourself, for two reasons:
- To confirm that the logging is working the way it should be working.
- To establish a base timeline.
At the end you should have four sysdiagnose logs:
- A customer one, with normal logging
- Your one, with normal logging
- A customer one, with extra NE logging
- Your one, with extra NE logging
Look at the normal logs to confirm the disparity in the timeline. Then look at the extra NE logging logs to confirm that you can still see the disparity in the timeline, despite the extra log pressure being applied by the extra NE logging.
If that’s the case you can then dig into the last two logs to see what’s actually happening. Specifically, focus on your logging subsystem to see what’s going on with your product and the NE subsystem (com.apple.networkextension) to see what’s going on with NE.
I suspect that all of this work will confirm that something ‘weird’ is happening on the device, something that’s not under your direct control. At which point you, or your customer, can file a bug with the details. Critically:
- The extra NE logging will help Apple investigate the issue.
- You can call out your log points as evidence that your content filter is not the cause of the problem.
If you do file a bug report, please post your bug number, just for the record.
*phew*
That’s a lot of work, but I’m hoping it’ll give you some good insight into this issue.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"