Regarding Long-Term Socket Connections in iOS Apps (TCP/UDP)

I am developing an app designed for use in hospital environments where public internet access may not be available. The app consists of two parts: the main app and a Local Connectivity Extension. Both components rely on socket connections (TCP and UDP) to maintain communication with a server. We have observed consistent disconnections occurring every 1-2 hours when app in the foreground (both extension and app), and would like to clarify Apple's guidelines regarding long-term socket connections. Specifically, is it permissible to keep a continuous socket connection (both TCP and UDP) active for an extended period? If so, are there any restrictions or best practices we should follow to ensure stable connectivity? Thank you for your assistance.

Answered by DTS Engineer in 808338022

I’m presuming that we’re talking iOS here. Let me know if that’s wrong.

AFAIK there’s nothing in the system that’ll prevent you from maintaining a long-term TCP connection (or UDP flow) as long your process doesn’t get suspended. If you’re doing this from an app that remains in the foreground, that app won’t get suspended and thus your networking will persist.

IMPORTANT Remember that locking the device moves your app to the background and thus may make it eligible for suspension.

There is one potential sticking point here, namely you should set UIRequiresPersistentWiFi. See The iOS Wi-Fi Lifecycle for an explanation as to why.

My experience is that this sort of problem is commonly caused by the network infrastructure rather than iOS itself. For example, if your network uses NAT then it’s common for the NAT gateway to ‘garbage collect’ mappings after some period of inactivity. And there are lots of other middleboxen that do similar things. And I regularly see issues like this where the limit is being imposed by the Wi-Fi infrastructure itself!

In situations like this it’s important to find out where the connection is tearing. As a first step, use an RVI packet trace to see what’s happening at the TCP/IP level. That’ll tell you which side of the connection is initiating the disconnect.

Share and Enjoy

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

I’m presuming that we’re talking iOS here. Let me know if that’s wrong.

AFAIK there’s nothing in the system that’ll prevent you from maintaining a long-term TCP connection (or UDP flow) as long your process doesn’t get suspended. If you’re doing this from an app that remains in the foreground, that app won’t get suspended and thus your networking will persist.

IMPORTANT Remember that locking the device moves your app to the background and thus may make it eligible for suspension.

There is one potential sticking point here, namely you should set UIRequiresPersistentWiFi. See The iOS Wi-Fi Lifecycle for an explanation as to why.

My experience is that this sort of problem is commonly caused by the network infrastructure rather than iOS itself. For example, if your network uses NAT then it’s common for the NAT gateway to ‘garbage collect’ mappings after some period of inactivity. And there are lots of other middleboxen that do similar things. And I regularly see issues like this where the limit is being imposed by the Wi-Fi infrastructure itself!

In situations like this it’s important to find out where the connection is tearing. As a first step, use an RVI packet trace to see what’s happening at the TCP/IP level. That’ll tell you which side of the connection is initiating the disconnect.

Share and Enjoy

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

Thank you for your previous response. I have a follow-up question regarding Local Push Connectivity. Since this is an extension that runs in the background, I’d like to know whether it’s possible to maintain a long-term TCP/UDP socket connection in this context. Can the extension keep such a connection active for a long time? Are there any specific restrictions or best practices related to Local Push Connectivity that we should follow to ensure stable connectivity? thanks

For reference:https://developer.apple.com/documentation/networkextension/local_push_connectivity

Thank you for your previous response. I have a follow-up question regarding Local Push Connectivity. Since this is an extension that runs in the background, I’d like to know whether it’s possible to maintain a long-term TCP/UDP socket connection in this context. Can the extension keep such a connection active for a long time? Are there any specific restrictions or best practices related to Local Push Connectivity that we should follow to ensure stable connectivity? thanks

As far as the system is concerned, it'll happily keep your connection open indefinately. The main problems people have with this API are with the larger network (and configuring it), not with the API itself. More specifically:

  1. Just so this is clear, your app has to "know" about the network it's going to work out without any information from the system. For example, there isn't any option to "work on whatever the current network is" or to ask the system about what WiFi networks are visible to it. I don't think this will be an issue for you, but I want to be clear about what's required.

  2. Physical network quality has a huge effect on how well this will work. On a well surveyed network with minimal deadzone, it works pretty well. On a poorly surveyed network with lots of dead zones... it doesn't.

  3. I've seen all sort of network configuration issues. For example:

  • The app would "spontaneously" disconnect after the screen was locked of 15-20 min, then reconnect as soon as the device was woken from sleep. I eventual determined that it was disconnecting... because that was EXACTLY what the Access Point had been configured to do. The AP was sending a disassociate command at specific intervals and the iPhone was doing exactly. In the foreground it caused small glitches (that were each to overlook) but overall activity triggered a reconnect, which got everything working again. In the background, the system stayed disconnected and went to sleep completely.

  • Some NAT hardwar doesn't handle long running, low volume connections very well. In the worst case, it close the upstream (server) side while leaving the downstream (your app) side of the connection "active". Your server will see the connections close immediately, but your app won't know anything is wrong until it tries to transmit something.

  1. Keep in mind that this extension is designed as a fairly "direct" replacement for APNS, not as a "general purpose" networking point. Just like APNS, "call notification" and "call handling" will need to be handled separately. Your extension maintains a connection your server uses to tell the device about incoming calls (or text messages) and your app creates it's own connection when the extension notifies it of an incoming call. I've seen a few developers try and force all networking through the extension (generally because that's how their server currently works) and it's never really worked.

  2. If you have an existing voip app, be aware that when the your app receives appPushManager:didReceiveIncomingCallWithUserInfo:, it's kept awake for FAR less time that PushKit does. You'll need to call "beginBackgroundTask" yourself to insure that your app stays awake long enough for your CallKit call to initialize (at which point, CallKit will keep you awake). This is basically what "all" background apps should be doing, but PushKit has an excessively long wake time.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Regarding Long-Term Socket Connections in iOS Apps (TCP/UDP)
 
 
Q