HTTP-запросы в Swift в 2025: от новичка до PRO за 20 минут

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему 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 МБ) и зависимостью от стороннего кода. Для прост

Блоги

Рекомендаций по книгам