Введение: Почему Swift-разработчики до сих пор гуглят HTTP-запросы?
Если вы когда-нибудь писали приложение на Swift, которое общается с сервером, вы точно сталкивались с этим вопросом. Казалось бы, что сложного? Отправил запрос, получил ответ. Но на практике каждый второй разработчик (даже с опытом) открывает Stack Overflow и ищет: "How to make HTTP request in Swift?". И это нормально.
Почему? Потому что экосистема Swift меняется. Ещё вчера все писали замыкания и делегаты. Сегодня — async/await. А завтра, возможно, появится что-то ещё. В 2025 году подходы к сетевым запросам устоялись, но дьявол, как всегда, в деталях: управление потоками, обработка ошибок, отмена запросов, тестирование.
В этой статье мы разберём реальные лучшие практики выполнения HTTP-запросов в Swift — от базового URLSession до продвинутых техник с async/await и библиотеками. Вы узнаете, как писать чистый, безопасный и производительный сетевой код. Поехали!
1. URLSession: Фундамент, который нужно знать каждому
Начнём с главного. В стандартной библиотеке Swift (Foundation) есть всё необходимое для работы с HTTP — это класс URLSession. Запомните: для 90% задач вам не нужны сторонние библиотеки. URLSession — это мощный, гибкий и нативный инструмент.
1.1. Простой GET-запрос (старый стиль)
Давайте вспомним, как это делалось раньше. Этот код вы всё ещё можете встретить в легаси-проектах:
let url = URL(string: "https://api.pythonlib.ru/users")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Ошибка: \(error.localizedDescription)")
return
}
guard let data = data else {
print("Нет данных")
return
}
// Работаем с data
print("Получено байт: \(data.count)")
}
task.resume()
Этот код работает. Но у него есть недостатки:
- Callback hell — если нужно сделать цепочку запросов, код становится нечитаемым.
- Утечки памяти — легко забыть про
weak selfв замыкании. - Сложно отменять — нужно хранить ссылку на
task.
В 2025 году мы пишем иначе.
1.2. Современный GET-запрос с async/await
Начиная с Swift 5.5 (iOS 15, macOS 12), у нас есть structured concurrency. Это меняет всё:
func fetchUsers() async throws -> [User] {
let url = URL(string: "https://api.pythonlib.ru/users")!
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw URLError(.badServerResponse)
}
return try JSONDecoder().decode([User].self, from: data)
}
Красота, правда? Никаких замыканий, никаких resume(). Код читается как синхронный, но выполняется асинхронно. Это стандарт де-факто в 2025 году.
2. POST-запрос: отправляем данные на сервер
Теперь давайте отправим данные. Допустим, нам нужно создать нового пользователя.
2.1. POST с JSON-телом
func createUser(name: String, email: String) async throws -> User {
let url = URL(string: "https://api.pythonlib.ru/users")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body = ["name": name, "email": email]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (data, response) = try await URLSession.shared.data(for: request)
// Обработка ответа...
return try JSONDecoder().decode(User.self, from: data)
}
Обратите внимание на строку data(for:) — это асинхронный метод для кастомных запросов. Мы сами собираем URLRequest с нужными полями.
3. Продвинутые техники: таймауты, кэширование, отмена
Просто отправить запрос мало. Настоящий профессионал думает о граничных случаях.
3.1. Таймауты и политика кэширования
Представьте, что сервер завис. Ваше приложение будет висеть вечно? Нет, если настроить таймаут:
var request = URLRequest(url: url)
request.timeoutInterval = 10 // 10 секунд
request.cachePolicy = .returnCacheDataElseLoad
Политика кэширования — мощный инструмент. .returnCacheDataElseLoad сначала покажет кэш, а потом обновит данные в фоне. Идеально для списков статей или каталогов.
3.2. Отмена запросов
Пользователь ушёл с экрана — запрос нужно отменить. С async/await это делается через Task:
var fetchTask: Task?
func startFetch() {
fetchTask = Task {
do {
let users = try await fetchUsers()
// обновляем UI
} catch {
// обрабатываем ошибку
}
}
}
func cancelFetch() {
fetchTask?.cancel()
}
Или используйте withTaskCancellationHandler для более тонкого контроля.
4. Alamofire: когда стандартных средств мало
Да, я сказал, что URLSession хватает для 90% задач. Но есть 10%, где удобнее использовать библиотеки. Alamofire — король в этой области. В 2025 году он всё ещё актуален, хотя конкуренты (Moya, Networking) дышат в спину.
4.1. Почему Alamofire?
- Цепочки запросов — встроенная поддержка последовательных и параллельных запросов.
- Интерцепторы — автоматическая подстановка токенов, рефреш авторизации.
- Загрузка файлов — прогресс, докачка.
- Тестирование — встроенные моки.
4.2. Пример с Alamofire
AF.request("https://api.pythonlib.ru/users",
method: .get,
headers: ["Authorization": "Bearer token"])
.validate()
.responseDecodable(of: [User].self) { response in
switch response.result {
case .success(let users):
print("Пользователи: \(users)")
case .failure(let error):
print("Ошибка: \(error)")
}
}
Но помните: за удобство вы платите размером бинарника (~2 МБ) и зависимостью от стороннего кода. Для прост