URLSession is an API for network requests in iOS and macOS, part of the Foundation framework that provides a unified interface for working with HTTP, HTTPS, background downloads, and WebSocket. It replaced the outdated NSURLConnection and became the primary networking tool for Apple developers. According to Apple Developer Documentation (2025), URLSession handles over 90% of network traffic in iPhone and iPad applications.
Key Takeaways
URLSession is an API for network operations introduced by Apple in iOS 7 and macOS 10.9. It replaced NSURLConnection and provided a more flexible and powerful model for working with the network. The library supports HTTP/1.1, HTTP/2, HTTP/3, and WebSocket, as well as background sessions for data loading.
The main advantage of URLSession over the old NSURLConnection is support for multiple simultaneous connections through a single session, configurable configurations, and the ability to pause and resume downloads. A session combines a group of network tasks with shared settings: caching, cookie policy, timeouts, TLS certificates.
In iOS, URLSession works with the system DNS cache, power management, and background processes. When the app is minimized, the system can continue loading data through a background session, and upon completion, notify the app via a completion handler. Background sessions are especially useful for downloading large files, updating content, and synchronizing data in document and media applications. To work with background sessions, you need to implement the URLSessionDelegate and pass a configuration identifier when creating the session — the system uses this identifier to restore the session after an app restart.
An important advantage of URLSession is support for HTTP/2 and HTTP/3. HTTP/2 multiplexing allows sending multiple requests over a single TCP connection, reducing latency and server load. HTTP/3, based on the QUIC protocol over UDP, provides even faster connection establishment by eliminating the TCP handshake.
The URLSession architecture consists of three key components: session configuration, the session itself, and tasks. Each component is responsible for its own aspect of network interaction, and their combination determines the application’s behavior when working with the network.
Configuration sets session parameters — from timeouts to caching policies. There are three types: .default (standard with disk cache), .ephemeral (without saving cache and cookies), and .background (for background downloads). In the configuration, you can specify the maximum number of connections per host, request and resource timeout, TLS policy, and proxy settings.
URLSessionTask is the base class for all network operations. The main subclasses are: URLSessionDataTask for GET and POST requests returning data in memory, URLSessionDownloadTask for downloading files to disk, URLSessionUploadTask for uploading files to the server, and URLSessionWebSocketTask for working with WebSocket since iOS 13.
The session delegate receives events about task progress: data reception, download completion, authentication errors, redirects. URLSessionDelegate and its sub-protocols allow intercepting TLS certificates during authentication, managing response caching through URLCache, and monitoring download progress for large files. Alternatively, closures (completion handlers) can be used for simple cases where intermediate event handling is not required.
The basic workflow with URLSession looks like this: create a configuration, create a session based on it, then create a task with a URL request through the session, and start the task. Let’s walk through an example GET request with a closure. This pattern is used in most iOS applications for fetching data from REST APIs, loading images, and interacting with cloud services.
let url = URL(string: "https://api.example.com/users")!
let session = URLSession.shared
let task = session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("Error: \(error!.localizedDescription)")
return
}
if let json = try? JSONSerialization.jsonObject(with: data) {
print("JSON: \(json)")
}
}
task.resume()
The example uses URLSession.shared — a singleton with .default configuration for simple requests. dataTask creates an asynchronous operation but does not start it — you must call resume(). The closure fires after the request completes and returns data, response, or error. For a custom session with your own configuration, use the URLSession(configuration:) initializer, which allows setting caching policies, timeouts, maximum connections, default HTTP headers, and proxy settings for all session tasks. This approach gives you full control over network behavior in your application.
URLSession supports file downloads with the ability to pause and resume, as well as background downloads. DownloadTask saves the file to a temporary directory, and in the completion closure, you need to move it to a permanent location.
let url = URL(string: "https://example.com/file.zip")!
let session = URLSession(configuration: .default)
let task = session.downloadTask(with: url) { location, _, _ in
guard let location = location else { return }
let destination = FileManager.default.temporaryDirectory
.appendingPathComponent("file.zip")
try? FileManager.default.moveItem(at: location, to: destination)
}
task.resume()
The location parameter in the closure points to a temporary file that needs to be moved, otherwise the system will delete it after the closure exits. To track progress, use the URLSessionDownloadDelegate with the didWriteData method, which passes the number of bytes written and the total file size — this data can be used to display a progress bar in the user interface. Background sessions with downloadTask allow you to continue downloading even after the app is minimized, and upon completion, the system calls the handler in AppDelegate.
Alamofire is a Swift library built on top of URLSession that provides a more declarative interface. The choice between the native API and Alamofire depends on the project’s complexity and team preferences.
| Criterion | URLSession | Alamofire |
|---|---|---|
| Dependencies | None, built into Foundation | Requires SPM or CocoaPods |
| Syntax | Imperative with closures | Declarative with chaining |
| JSON Handling | Manual JSONSerialization | responseDecodable with Codable |
| Interceptors | Through delegates | RequestInterceptor, EventMonitor |
| Progress | Through delegate | downloadProgress, uploadProgress |
For simple projects, URLSession is sufficient and adds no dependencies. Alamofire is justified in projects with many network requests, complex error handling, and a need for concise syntax. Both technologies share the common URLSessionConfiguration foundation and are compatible with each other. When choosing, consider that URLSession is fully controlled by Apple and updates with iOS, while Alamofire requires the library developers to maintain compatibility with new OS versions.
Modern Swift supports async/await, which makes network requests more readable compared to closures. Let’s look at the same GET request using asynchronous syntax.
func fetchUsers() async throws -> [User] {
let url = URL(string: "https://api.example.com/users")!
let (data, _) = try await URLSession.shared.data(from: url)
let users = try JSONDecoder().decode([User].self, from: data)
return users
}
The data(from:) method is available since iOS 15 and macOS 12. It returns a tuple (Data, URLResponse) and throws an error on network issues. JSONDecoder with a Codable model automatically deserializes the response — this replaces the cumbersome JSONSerialization from the previous example.
For POST requests with a JSON body, URLRequest is used, where you configure the method, Content-Type headers, and the request body as Data. Async/await makes this process concise and clear, and error handling is simple through a do-catch block.
func createUser(name: String) async throws -> User {
var request = URLRequest(url: URL(string: "https://api.example.com/users")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body = try JSONEncoder().encode(["name": name])
request.httpBody = body
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(User.self, from: data)
}
Frequently Asked Questions
URLSession is the modern replacement for NSURLConnection, introduced in iOS 7. The main differences include support for multiple connections through sessions, task suspension and resumption, background downloads, and WebSocket. NSURLConnection is deprecated and not recommended for use in new projects.
To track progress, use the URLSessionDownloadDelegate with the urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite) method. Alternatively, for dataTask, you can subscribe to URLSessionTaskDelegate and receive updates via didSendBodyData.
Background downloads are configured through the .background(withIdentifier:) configuration. The app registers a completion handler in AppDelegate and receives results even after being minimized or closed. The system manages the download and notifies the app through the sessionDidFinishEvents delegate.
Yes, since iOS 13, URLSession supports WebSocket through the URLSessionWebSocketTask class. It is created with the webSocketTask(with:) method and provides send, receive methods with support for text and binary messages, as well as ping/pong for keeping the connection alive.
Timeouts are configured through URLSessionConfiguration: the timeoutIntervalForRequest property (waiting for a response to a request) and timeoutIntervalForResource (maximum time for the entire download). Defaults: 60 seconds for request and 7 days for resource.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also