I’m developing an app designed for hospital environments, where public internet access may not be available. The app includes two components: the main app and a Local Connectivity Extension. Both rely on persistent TCP socket connections to communicate with a local server.
We’re observing a recurring issue where the extension’s socket becomes unresponsive every 1–3 hours, but only when the device is on the lock screen, even if the main app remains in the foreground. When the screen is not locked, the connection is stable and no disconnections occur.
❗ Issue Details:
• What’s going on: The extension sends a keep-alive ping packet every second, and the server replies with a pong and a system time packet.
• The bug: The server stops receiving keep alive packets from the extension.
• On the server, we detect about 30 second gap on the server, a gap that shows no packets were received by the extension. This was confirmed via server logs and Wireshark).
• On the extension, from our logs there was no gap in sending packets. From it’s perspective, all packets were sent with no error.
• Because no packet are being received by the server, no packets will be sent to the extension. Eventually the server closes the connection due to keep-alive timeout.
• FYI we log when the NEAppPushProvider subclass sleeps and it did NOT go to sleep while we were debugging.
🧾 Example Logs:
Extension log:
2025-03-24 18:34:48.808 sendKeepAliveRequest()
2025-03-24 18:34:49.717 sendKeepAliveRequest()
2025-03-24 18:34:50.692 sendKeepAliveRequest()
... // continuous sending of the ping packet to the server, no problems here
2025-03-24 18:35:55.063 sendKeepAliveRequest()
2025-03-24 18:35:55.063 keepAliveTimer IS TIME OUT... in CoreService. // this is triggered because we did not receive any packets from the server
Server log:
2025-03-24 18:34:16.298 No keep-alive received for 16 seconds... connection ID=95b3... // this shows that there has been no packets being received by the extension ...
2025-03-24 18:34:30.298 Connection timed out on keep-alive. connection ID=95b3... // eventually closes due to no packets being received
2025-03-24 18:34:30.298 Remote Subsystem Disconnected {name=iPhone|Replica-Ext|...}
✅ Observations:
• The extension process continues running and logging keep-alive attempts.
• However, network traffic stops reaching the server, and no inbound packets are received by the extension.
• It looks like the socket becomes silently suspended or frozen, without being properly closed or throwing an error.
❓Questions:
• Do you know why this might happen within a Local Connectivity Extension, especially under foreground conditions and locked ?
• Is there any known system behavior that might cause the socket to be suspended or blocked in this way after running for a few hours?
Any insights or recommendations would be greatly appreciated.
Thank you!
The client detects the disconnection sometime between 30 seconds and 1 minute, which is the time gap of seemingly non-activity. (No logs from any services occured)
So, the first thing you need to be CERTAIN of here is that this is all happening inside the same extension provider instance. From past experience, I've seen many cases which looked like "the connection is disconnecting" when the actual issue was "the device is dropping on/off Wifi". My preferred technique here is to have your logging library include current pid (process id) in every log message, typically as the first value in the log message. If everything is formatted properly, it's easy to filter out when scanning an extended log but any change still tends to "jump out" when your scanning a log.
Based on the time delay you're describing, I suspect this is actually a Wifi issue, though it's possible that CFStream itself is the issue.
The extension uses CFStream to establish a TCP connection with the server, primarily for receiving real-time notifications. To keep the connection stable, it sends a keep-alive packet every second.
You should not be using CFStream for networking at ALL. CFSocketStream has been deprected since 2021, but we've telling people NOT to use CFStream for networking ever since we introduced the Network framework in iOS 10. At this point, I can't really have ANY confidence in how it will behave, particularly in a specialized context like a connectivity extension.
Note that we specifically document that the Network framework should be used by network extensions in the document "In-Provider Networking". As a side comment, the deprecations on the rest of that page are a side effect of how the binding between Swift and the network framework have evolved over time, NOT a "real" issue with it's actual implementation.
The goal is to ensure the extension’s TCP connection remains active and reliable in the background. Which value best supports that behavior?
These values primarily effect packet QOS, which is unlikely to matter in a LAN setting where the network is unlikely to ever be close to saturation. However, in the network framework "nw_service_class_best_effort" or "nw_service_class_signaling" are what I'd probably use.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware