URLSessionConfiguration Quick Guide

Are you relying on the default URLSession configuration for your network requests? You might want to check what the defaults are and decide if they are right for your app.

Creating a URLSession

Before I get into session configuration let’s review the different ways to create a URLSession. In order of increasing complexity (and flexibility):

Use a shared session

The quickest way to fire up a network session and start sending requests is to use the shared singleton session:

let session = URLSession.shared
let task = session.dataTask(with: url) { data, response, error in
  // handle result in completion handler
}
task.resume()

The shared session comes with a number of limitations:

  • You cannot configure it.
  • You cannot set a delegate to interact with the session.
  • You cannot use it to perform background network requests.

Use a default session

The important difference between a default session and the shared session is that you can configure it. You do that by first creating a URLSessionConfiguration object:

let configuration = URLSessionConfiguration.default
// change the default configuration
// before creating the session
let session = URLSession(configuration: configuration)

We’ll see why you might want to configure the session in a moment. Note that when you create a session it copies the session configuration object. Changing the configuration after you have created the session has no effect.

Using a delegate and queue

For full control you can specify a configuration, delegate and optionally an operation queue to perform the delegate callbacks on. The delegate and queue are both optional:

let session = URLSession(configuration: configuration,
                              delegate: delegate,
                         delegateQueue: nil)

The session delegate can use any of the optional methods in the URLSessionDelegate and URLSessionTaskDelegate protocols. This is useful for handling authentication failures, redirects, progress updates, etc.

URLSessionConfiguration Options

The defaults may get you started but you’ll need to customise the configuration if you want to do any of the following:

  • Change the default request and response timeouts.
  • Make the session wait for connectivity to be established.
  • Prevent your app from using the network when in low data mode or using expensive cellular connections.
  • Change from using the app’s shared URL cache.
  • Add additional HTTP headers to all requests.
  • Create ephemeral or background sessions.

Unless I mention otherwise these configuration properties have generally been available since iOS 7 or iOS 8.

Request Timeout

configuration.timeoutIntervalForRequest = 30

The timeout interval, in seconds, that a task will wait for data to arrive. The timer resets each time new data arrives. Default is 60 seconds. Don’t choose a value so small that your requests timeout on slower networks.

Resource Timeout

configuration.timeoutIntervalForResource = 300

The timeout interval, in seconds, that a task will wait for the whole resource request to complete. The default value is 7 days which might be longer than you want…

Waits For Connectivity (iOS 11)

configuration.waitsForConnectivity = true

Causes the session to wait for connectivity instead of failing immediately. This is preferable to testing for reachability. Waiting allows time for connectivity to be established, for example via a VPN or WiFi when cellular access is not allowed. Added in iOS 11, default is false.

If you’re using a delegate, the session calls urlSession(_:taskIsWaitingForConnectivity:) when waiting for connectivity.

The timeoutIntervalForResource property determines how long the session will wait for connectivity. (Remember that defaults to 7 days). Background sessions always wait for connectivity.

Allows Cellular Access

configuration.allowsCellularAccess = false

Is the session allowed to connect over a cellular network. Default is true. See “allows expensive network access” for an alternative approach (iOS 13 and later).

Allows Low Data Mode Access (iOS 13)

configuration.allowsConstrainedNetworkAccess = false

Is the session allowed to connect using a constrained network interface. Added in iOS 13. Default is true. A constrained network interface is one where the user turns on “Low Data Mode” in the device settings.

Allows Expensive Network Access (iOS 13)

configuration.allowsExpensiveNetworkAccess = false

Is the session allowed to connect over an “expensive” network interface. Added in iOS 13. Default is true. The system decides what an expensive network is but typically it’s a cellular or personal hotspot.

To prevent non-essential network activity over expensive and constrained networks combine with allowsConstrainedNetworkAccess:

configuration.allowsConstrainedNetworkAccess = false
configuration.allowsExpensiveNetworkAccess = false

Further combine with waitsForConnectivity to have the session wait until a non-constrained, non-expensive network is available:

configuration.waitsForConnectivity = true
configuration.allowsConstrainedNetworkAccess = false
configuration.allowsExpensiveNetworkAccess = false

Caching

The default session configuration uses the app shared URL cache object:

let shared = URLCache.shared

print(shared.memoryCapacity) // 512000 (512Kb)
print(shared.diskCapacity)   // 10000000 (10Mb)

print(shared.currentMemoryUsage)
print(shared.currentDiskUsage)

// Increase memory cache size
shared.memoryCapacity = 500_000_000 // 500Mb

You can also access the cache via the session configuration. This is an optional, setting the property to nil disables caching:

configuration.urlCache = nil

You can use your own session specific cache if you need it:

let cache = URLCache(memoryCapacity: 500_000_000, 
                       diskCapacity: 1_000_000_000)
configuration.urlCache = cache

HTTP Headers

If you need to add custom HTTP headers to all of your requests add them to the session configuration httpAdditionalHeaders dictionary. For example, to add your own user agent header:

configuration.httpAdditionalHeaders = ["User-Agent": "MyApp 1.0"]

Creating an ephemeral session

An ephemeral, or private, session keeps cache data, credentials or other session related data in memory. It’s never written to disk which helps protects user privacy. You destroy the session data when you invalidate the session. This is similar to how a web browser behaves when private browsing.

You could create an ephemeral session by configuring a default session with private cookie storage and cache objects but it’s easier to use the convenience method:

let configuration = URLSessionConfiguration.ephemeral
// other configuration settings
let ephemeralSession = URLSession(configuration: ephemeral)

Creating a background session

For network tasks that you want to run in the background use a background configuration:

let configuration = URLSessionConfiguration.background(
                    withIdentifier: "Downloader")

For non-urgent tasks make the configuration discretionary so the system can decide when to start the transfer:

configuration.isDiscretionary = true

Background tasks always wait for connectivity but you might want to change the default value for timeoutIntervalForResource to something less than 7 days.

Read More