Avatar ·

Как выполнить HTTP-запрос в Swift: пошаговое руководство для iOS-разработчиков

Привет! Я новичок в Swift и iOS-разработке, пытаюсь сделать простой GET-запрос к API, но ничего не работает. Мой код выглядит так: ```swift let url = URL(string: "https://api.example.com/users") let request = URLRequest(url: url!) let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in print(data) } dataTask.resume() ``` В консоль выводится `nil`. Я не понимаю, почему данные не приходят. Пробовал также использовать `URLSession.shared.dataTask(with: url)` — результат тот же. Я прочитал, что нужно использовать `URLSession`, но примеры в интернете слишком сложные: там какие-то замыкания, обработка ошибок, JSONDecoder... Мне нужно просто получить строку с сервера и вывести её. Подскажите, как правильно сделать HTTP-запрос в Swift? Желательно с минимальным кодом и пояснениями для новичка. Заранее спасибо! <<>>

Основная проблема: вы не обрабатываете ответ и не извлекаете данные

В вашем коде вы просто печатаете объект data, который может быть nil даже при успешном запросе. Нужно правильно обработать замыкание и преобразовать данные в строку.

Правильный минимальный GET-запрос

import Foundation

let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!

let task = URLSession.shared.dataTask(with: url) { data, response, error in
    // 1. Проверяем ошибки сети
    if let error = error {
        print("Ошибка запроса: \(error.localizedDescription)")
        return
    }
    
    // 2. Проверяем HTTP-статус ответа
    guard let httpResponse = response as? HTTPURLResponse,
          (200...299).contains(httpResponse.statusCode) else {
        print("Ошибка сервера: неверный статус-код")
        return
    }
    
    // 3. Проверяем, что данные есть
    guard let data = data else {
        print("Нет данных в ответе")
        return
    }
    
    // 4. Преобразуем Data в строку
    if let responseString = String(data: data, encoding: .utf8) {
        print("Ответ сервера:\n\(responseString)")
    } else {
        print("Не удалось декодировать данные в строку")
    }
}

task.resume()

Разбор кода по шагам:

  • Создание URL — всегда используйте guard let или проверяйте опциональность, чтобы избежать force unwrap (!)
  • dataTask — асинхронный метод, который выполняется в фоновом потоке
  • Обработка ошибок — проверяйте error, статус-код и наличие данных
  • Преобразование данных — Data нужно декодировать в строку или JSON

Более современный подход с async/await (iOS 15+)

func fetchData() async {
    guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1") else {
        print("Неверный URL")
        return
    }
    
    do {
        let (data, response) = try await URLSession.shared.data(from: url)
        
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
            print("Ошибка сервера")
            return
        }
        
        if let responseString = String(data: data, encoding: .utf8) {
            print(responseString)
        }
    } catch {
        print("Ошибка запроса: \(error.localizedDescription)")
    }
}

// Вызов в асинхронном контексте
Task {
    await fetchData()
}

Работа с JSON (самый частый случай)

struct User: Codable {
    let id: Int
    let name: String
    let email: String
}

func fetchUser() {
    let url = URL(string: "https://jsonplaceholder.typicode.com/users/1")!
    
    URLSession.shared.dataTask(with: url) { data, _, error in
        guard let data = data, error == nil else {
            print("Ошибка: \(error?.localizedDescription ?? "неизвестная")")
            return
        }
        
        do {
            let user = try JSONDecoder().decode(User.self, from: data)
            print("Пользователь: \(user.name), email: \(user.email)")
        } catch {
            print("Ошибка декодирования JSON: \(error)")
        }
    }.resume()
}

Важные советы для новичков:

  • Всегда добавляйте Info.plist разрешение на HTTP (или используйте HTTPS)
  • Для UI-обновлений после запроса используйте DispatchQueue.main.async
  • Не забывайте вызывать resume() — без него запрос не начнётся
  • Используйте URLSession.shared для простых запросов, кастомную сессию — для сложных

Почему ваш код не работал:

Вы печатали data как объект Data?. В консоли это отображается как nil, даже если данные есть. Нужно обязательно преобразовать Data в читаемый формат через String(data:encoding:) или JSONDecoder.

<<>>

Альтернативный подход: используйте библиотеку Alamofire

Если вам нужно часто делать запросы и не хочется писать boilerplate-код, попробуйте популярную библиотеку Alamofire. Она сильно упрощает работу с сетью.

Установка через Swift Package Manager или CocoaPods:

// Пример GET-запроса с Alamofire
import Alamofire

AF.request("https://jsonplaceholder.typicode.com/posts/1").responseString { response in
    switch response.result {
    case .success(let value):
        print("Ответ: \(value)")
    case .failure(let error):
        print("Ошибка: \(error)")
    }
}

Для работы с JSON:

struct Post: Decodable {
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

AF.request("https://jsonplaceholder.typicode.com/posts/1")
    .responseDecodable(of: Post.self) { response in
        if let post = response.value {
            print("Пост: \(post.title)")
        }
    }

Преимущества Alamofire:

  • Автоматическая обработка ошибок и статус-кодов
  • Поддержка параметров, заголовков и кодирования
  • Встроенное логирование запросов
  • Простая загрузка/отправка файлов

Однако для учебных целей и простых проектов я рекомендую сначала освоить нативный URLSession — так вы лучше поймёте, как работают сетевые запросы под капотом.