Introduction: why proper HTTP requests are the foundation of any application
In modern mobile and desktop development with Swift, it's rare to find an application that works completely autonomously. Almost any iOS or macOS application interacts with remote servers: loading data, sending forms, synchronizing content. An HTTP client is a key component whose quality affects not only performance, but also the security and stability of the entire application.
In this article, we will break down how to create a reliable HTTP client in Swift using the built-in Foundation framework. You will learn how to properly configure URLSession, handle errors, work with JSON, and use modern approaches such as async/await. The material will be useful both for beginners just learning Swift and for experienced developers looking to systematize their knowledge.
1. URLSession Basics: the foundation of network interaction
In Swift, the primary tool for working with HTTP is the URLSession class. It is part of the Foundation framework and provides a flexible API for sending requests and receiving responses. URLSession supports several configurations, allowing you to adapt behavior to specific tasks.
Creating and configuring a session
The first step is to create an instance of URLSession with a specific configuration. There are three types of configurations:
.default— standard configuration with caching and cookie persistence;.ephemeral— without saving data to disk (cookies, cache stored only in memory);.background— for background tasks that can continue even after the application is closed.
Example of creating a session with a custom configuration:
let configuration = URLSessionConfiguration.defaultconfiguration.timeoutIntervalForRequest = 30configuration.waitsForConnectivity = true
let session = URLSession(configuration: configuration)Pay attention to the waitsForConnectivity parameter: if set to true, the request will not fail with an error when there is no network, but will wait for the connection to be restored. This is especially important for mobile applications.
Basic GET request
The simplest way to execute a request is to use the dataTask(with:) method. Here is a minimal example:
guard let url = URL(string: "https://api.example.com/users") else { return }
let task = session.dataTask(with: url) { data, response, error in if let error = error { print("Request error: \\(error.localizedDescription)") return } guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { print("Invalid server response") return } guard let data = data else { print("No data in response") return } // Data processing print("Received \\(data.count) bytes")}task.resume()Important point: don't forget to call resume(), otherwise the request won't start. This is one of the most common mistakes beginners make.
2. Working with JSON: from data to objects
Most modern APIs return data in JSON format. In Swift, the Codable protocol is used for working with JSON, which combines Encodable and Decodable. This allows you to easily convert data to models and back.
Creating a model and decoding
Suppose the server returns a list of users in this format:
[ { "id": 1, "name": "Ivan Petrov", "email": "ivan@example.com" }]Let's create the corresponding structure:
struct User: Codable