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.