Mastering Network Reachability in iOS

This article delineates the common mistakes with Network Reachability on Apple platforms and suggests strategies to navigate these challenges.

Mazvydas Katinas
4 min readOct 13, 2023

The demand for highly responsive and reliable apps is paramount. A significant facet of achieving this lies in an app’s adeptness in managing network reachability, ensuring seamless user experience irrespective of the network conditions.

The Pre-flight Reachability Checks Myth

A prevalent misconception among developers is the necessity to confirm network availability before initiating a network request. This notion, while seemingly logical, contradicts Apple’s directive:

“Always attempt to make a connection. Do not guess whether network service is available or cache that determination.”

- Start
- Check Network Reachability?
- Yes -> Possible incorrect result
- No -> Make a request

Many apps rely on tools like SCNetworkReachability, NWPathMonitor, or other frameworks to decide on sending a request. However, this approach is flawed, as it could erroneously report a lack of connectivity, despite the opposite being true.

Apple’s recommended approach emphasizes using reachability status post-failure. For instance, upon a request failure, auto-retrying the request upon detecting network availability or providing user feedback like “It seems you’re offline” without stalling the initial request.

Embracing the ‘Wait for Connectivity’ Model

Apple’s Networking frameworks are optimized for a ‘Wait for Connectivity’ model, wherein if an active request encounters missing connectivity, it pauses and resumes automatically once the connectivity is restored.

This model, as opposed to pre-flight reachability checks, validates the network conditions in real-time rather than at a single moment, accounting for transient network interruptions.

To help with that Apple provides two significant timeout properties within the URLSessionConfiguration class, namely timeoutIntervalForRequest and timeoutIntervalForResource.

let configuration = URLSessionConfiguration.default
configuration.waitsForConnectivity = true
configuration.timeoutIntervalForRequest = 60
configuration.timeoutIntervalForResource = 60 * 60

timeoutIntervalForRequest

This property defines the maximum amount of time a request should wait to receive additional data once the initial connection has been established. The timer associated with this timeout gets reset every time new data arrives, making it suitable for regular tasks where data is expected to be received continuously over time.

// Timeout interval is set to 60 seconds
configuration.timeoutIntervalForRequest = 60

This setting is crucial for maintaining a brisk user experience, as it ensures that the app does not hang indefinitely waiting for a response, especially in scenarios where network conditions are less than ideal.

timeoutIntervalForResource

On the other hand, timeoutIntervalForResource specifies the maximum amount of time a request should wait for the entire resource to be transferred. This property is highly beneficial for handling long-term tasks such as file uploads or downloads, which may require a substantial amount of time to complete.

// Timeout interval is set to 1 hour
configuration.timeoutIntervalForResource = 60 * 60

The default value for this property is set to 7 days, a testament to its design for handling long-term network tasks. Unlike timeoutIntervalForRequest, this timer does not reset with the arrival of new data, as it’s designed to ensure the complete transfer of a resource within the specified time frame.

These properties should be configured judiciously based on the nature of the network tasks at hand. For instance, a simple API request to fetch data may require a shorter timeout interval compared to a large file upload task. Adhering to the principle of setting shorter timeouts for regular tasks and longer timeouts for resource-intensive tasks can lead to an optimized network operation, ensuring that the app remains responsive while making efficient use of network resources.

Cellular Connectivity and Error Management

Not including cellular data usage for network requests is another common misstep. A more precise control can be attained using the allowsCellularAccess flag, alongside handling networking errors effectively to inform users about unstable connections.

var myRequest = URLRequest(url: URL(string: "https://www.example.com")!)
myRequest.useCellularData = true

// Error handling snippet
extension Error {
var isOtherConnectionError: Bool {
NSURLErrorConnectionFailureCodes.contains(_code)
}
}

Other Significant Options

With iOS 13, Apple introduced a couple of compelling properties within the URLSessionConfiguration class, aiming to provide you with more granular control over network operations. These properties are allowsExpensiveNetworkAccess and allowsConstrainedNetworkAccess. They come in handy in managing network requests more efficiently, especially in scenarios where network resources are scarce or expensive.

allowsExpensiveNetworkAccess

This property provides a mechanism to prevent network tasks from using network interfaces deemed “expensive” by the system, such as cellular or other metered networks.

let configuration = URLSessionConfiguration.default
// Disallowing expensive network access
configuration.allowsExpensiveNetworkAccess = false
let session = URLSession(configuration: configuration)

By setting allowsExpensiveNetworkAccess to false, you can prevent network tasks from utilizing expensive or metered networks, thereby potentially saving user data and avoiding extra costs.

allowsConstrainedNetworkAccess

On the flip side, allowsConstrainedNetworkAccess is tailored for scenarios when the device is in Low Data Mode. Low Data Mode is a feature where users can opt to reduce network data usage.

// Disallowing network access in Low Data Mode
configuration.allowsConstrainedNetworkAccess = false

By tweaking `allowsConstrainedNetworkAccess`, you can choose to halt or allow network operations when the device is in Low Data Mode, ensuring the app adheres to the user’s preference for reduced data usage.

Conclusion

Mastering network reachability on Apple platforms entails avoiding pre-flight checks, leveraging the ‘Wait for Connectivity’ model, and employing the right configurations and error handling mechanisms. By adhering to these strategies, you can significantly enhance the network robustness of their apps, ensuring a superior user experience irrespective of the network conditions.

Happy coding!

--

--

No responses yet