What do the URLSession configurations control, and how do default, ephemeral and background sessions differ?
Predict first
What are the defaults of a default configuration and of a background one?
import Foundation
let d = URLSessionConfiguration.default
print(d.timeoutIntervalForRequest, d.timeoutIntervalForResource, d.waitsForConnectivity, d.allowsCellularAccess, d.httpMaximumConnectionsPerHost)
let b = URLSessionConfiguration.background(withIdentifier: "x")
print(b.sessionSendsLaunchEvents, b.isDiscretionary)
- 60.0 604800.0 false true 6, then true false
- 30.0 60.0 true true 4, then false false
- 60.0 604800.0 true true 6, then true true
Mental model. A configuration is the settings card you hand over when opening an account: opening hours, limits, where statements are kept. You cannot edit the card afterwards, only open a new account.
A URLSession is created from a URLSessionConfiguration that fixes its behaviour: timeouts, cache and cookie storage, credential storage, cellular and constrained-network access, connection limits, protocol classes, extra headers and more. The configuration is copied at creation and cannot be changed afterwards, so make a new session (or per-request settings on URLRequest) for different behaviour. Three presets. .default uses the on-disk cache, cookie storage and credential storage. .ephemeral keeps everything in memory and writes nothing to disk, which suits private browsing and tests. .background(withIdentifier:) hands transfers to a system daemon that continues when your app is suspended or even terminated and relaunches the app to deliver results; it has restrictions (uploads must come from a file, only download, upload and the like are supported, delegate-based reporting, no async data(for:) convenience for work that outlives the process) and its mechanics are covered with the platform topic.
Know the defaults. A request times out after 60 seconds of inactivity (timeoutIntervalForRequest: time between packets, not total), and a resource after seven days (timeoutIntervalForResource: the whole transfer). waitsForConnectivity is false, so a request fails immediately when there is no network unless you set it true (then the task waits and you learn about it through urlSession(_:taskIsWaitingForConnectivity:)). There are about six connections per host for HTTP/1.1, while HTTP/2 and HTTP/3 multiplex many requests over one. A background session’s sessionSendsLaunchEvents is true and isDiscretionary false by default. Reuse one session per configuration for the app lifetime (each session has its own connection pool, so creating one per request defeats connection reuse), and remember that a session with a delegate retains it strongly until you call invalidateAndCancel() or finishTasksAndInvalidate(), which is a classic leak.
Requests time out after 60 seconds of inactivity, a resource after 7 days (604,800 s), there is no waiting for connectivity, and background sessions send launch events but are not discretionary.
Common traps
- Creating a new
URLSessionfor every request. Each has its own connection pool, so connections are never reused. - Setting a delegate and never invalidating the session. The session retains the delegate strongly, so it leaks.
They might follow up with
- What does the 60-second request timeout measure? Inactivity between packets, not total time;
timeoutIntervalForResourcebounds the whole transfer. - When do you use ephemeral? When nothing should persist (private mode, tests).
Related questions
- 12.1.2 How do you make a request with URLSession, and why is a 404 not an error?
- 12.4.2 How do you do multipart uploads, large uploads and downloads, and show progress?
- 6.5.1 An app goes to the background. What can it still do, and how do you choose a mechanism?
How do you make a request with URLSession, and why is a 404 not an… →