Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое URLSession?
URLSession — это фундаментальный API в iOS/macOS фреймворке Foundation, предназначенный для выполнения сетевых операций: загрузки данных, загрузки и выгрузки файлов, а также взаимодействия с веб-сокетами по протоколам HTTP/HTTPS. Он представляет собой мощную и гибкую абстракцию над сетевыми запросами, пришедшую на смену устаревшему NSURLConnection в iOS 7 и macOS 10.9. Его основная задача — управлять группами (сессиями) связанных сетевых задач, предоставляя тонкий контроль над кешированием, аутентификацией, управлением cookies и поведением в фоновом режиме.
Ключевые преимущества URLSession:
- Гибкость конфигурации: Разные типы сессий (default, ephemeral, background) для различных сценариев.
- Асинхронность по умолчанию: Все задачи выполняются асинхронно, не блокируя главный поток.
- Поддержка фоновых загрузок: Продолжение загрузки данных даже когда приложение свернуто или находится в фоне.
- Делегирование: Полный контроль над процессом запроса и ответа через методы делегата.
- Интеграция с современными API: Полная поддержка
async/await(начиная с iOS 15),Combine(с iOS 13), и классических completion handlers.
Архитектура и основные компоненты
Архитектура строится вокруг трех основных сущностей:
-
URLSession (Сама сессия): Координирует группу связанных сетевых задач. Создается с помощью
URLSessionConfiguration, которая определяет поведение всех задач в этой сессии (таймауты, политики кеширования, дополнительные HTTP-заголовки и т.д.). -
URLSessionTask (Задача): Абстрактный класс, представляющий единицу работы (один сетевой запрос). Существуют конкретные типы задач для разных целей:
* **URLSessionDataTask**: Для загрузки данных в память (например, JSON или небольшого изображения). Самый часто используемый тип.
* **URLSessionDownloadTask**: Для загрузки файлов непосредственно на диск с поддержкой возобновления при обрыве соединения.
* **URLSessionUploadTask**: Для загрузки (upload) данных (например, файлов) на сервер.
* **URLSessionStreamTask** (редко используется): Для установки TCP/IP-соединений.
* **URLSessionWebSocketTask** (iOS 13+): Для работы с WebSocket.
- URLSessionDelegate и его специализации: Набор протоколов, которые позволяют отслеживать и реагировать на события в жизненном цикле запроса: получение данных по частям, редиректы, проблемы с аутентификацией, завершение загрузки файла.
Типы конфигураций сессии (URLSessionConfiguration)
// 1. Конфигурация по умолчанию. Использует глобальный кеш и хранилище кук.
let defaultConfig = URLSessionConfiguration.default
// 2. Эфемерная конфигурация. Не сохраняет данные на диск (кеш, куки).
// Идеально для приватных сессий, например, в режиме инкогнито.
let ephemeralConfig = URLSessionConfiguration.ephemeral
// 3. Фоновая конфигурация. Позволяет выполнять загрузки/выгрузки,
// даже когда приложение не активно. Идентификатор должен быть уникальным.
let backgroundConfig = URLSessionConfiguration.background(
withIdentifier: "com.yourapp.backgroundDownload"
)
// Настройка конфигурации
defaultConfig.timeoutIntervalForRequest = 30.0
defaultConfig.httpAdditionalHeaders = ["User-Agent": "MyApp/1.0"]
defaultConfig.waitsForConnectivity = true // Ждать появления сети, если она недоступна
Практическое использование
1. Простой запрос с Completion Handler (классический способ)
guard let url = URL(string: "https://api.example.com/data") else { return }
// Создаем сессию с конфигурацией по умолчанию
let session = URLSession.shared // Используем общую (синглтон) сессию для простых запросов
let task = session.dataTask(with: url) { data, response, error in
// Этот блок выполняется в фоновом потоке!
// 1. Проверяем на ошибку сети
if let error = error {
print("Network error: \(error)")
return
}
// 2. Проверяем HTTP-статус код
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("Server returned an error")
return
}
// 3. Обрабатываем полученные данные
guard let data = data else { return }
do {
let decodedObject = try JSONDecoder().decode(MyModel.self, from: data)
// Для обновления UI обязательно переходим на главный поток
DispatchQueue.main.async {
self.updateUI(with: decodedObject)
}
} catch {
print("Decoding error: \(error)")
}
}
// Запускаем задачу (без этого запрос не выполнится!)
task.resume()
2. Запрос с использованием современного async/await (iOS 15+)
func fetchData() async {
guard let url = URL(string: "https://api.example.com/data") else { return }
// Используем общую сессию
let session = URLSession.shared
do {
// Вызов приостанавливает текущую таску, но не блокирует поток
let (data, response) = try await session.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
let decodedObject = try JSONDecoder().decode(MyModel.self, from: data)
// Так как async-функции могут быть вызваны с MainActor, проверяем контекст
await MainActor.run {
self.updateUI(with: decodedObject)
}
} catch {
print("Request failed: \(error)")
}
}
3. Использование делегатов для отслеживания прогресса загрузки
class DownloadManager: NSObject, URLSessionDownloadDelegate {
lazy var session: URLSession = {
let config = URLSessionConfiguration.default
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
func startDownload(from urlString: String) {
guard let url = URL(string: urlString) else { return }
let task = session.downloadTask(with: url)
task.resume()
}
// Делегатный метод: вызывается при завершении загрузки
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) {
// Временный файл находится по адресу `location`
// Мы должны переместить его в постоянное хранилище (например, в папку Documents)
let fileManager = FileManager.default
let docsPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let permanentPath = docsPath.appendingPathComponent("downloadedFile.zip")
do {
try fileManager.moveItem(at: location, to: permanentPath)
print("File saved to: \(permanentPath)")
} catch {
print("Error saving file: \(error)")
}
}
// Делегатный метод: вызывается для отчета о прогрессе
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64) {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
DispatchQueue.main.async {
self.progressView.progress = progress
}
}
}
Важные аспекты и лучшие практики
- Управление памятью: Ссылка на сессию (
URLSession) должна жить дольше, чем ее задачи. Если вы создаете сессию с делегатом, храните сильную ссылку на нее (например, в свойстве класса), иначе делегатные методы перестанут вызываться. - Отмена и приостановка: Задачи можно отменить (
task.cancel()) или приостановить (task.suspend()). Отмена может быть как "мягкой" (позволяет завершиться completion handler), так и "жесткой" (task.cancel(byProducingResumeData:)дляURLSessionDownloadTask). - Таймауты: Настраиваются на уровне конфигурации сессии (
timeoutIntervalForRequest- для одного пакета данных,timeoutIntervalForResource- для всей задачи). - Кеширование: Политика кеширования (
requestCachePolicy) определяет, когда использовать кешированные данные. Работает совместно сURLCache, который можно настраивать под свои нужды. - Безопасность (SSL/TLS, App Transport Security): URLSession автоматически применяет современные стандарты безопасности. Для работы с небезопасными HTTP-соединениями или кастомными SSL-сертификатами требуется настройка
ATSвInfo.plistи реализация соответствующих делегатных методов (urlSession(_:didReceive:completionHandler:)).
Итог: URLSession — это не просто "менеджер запросов", а комплексная система для сетевого взаимодействия. Его правильное использование, с учетом типа задачи (простой запрос, большая загрузка, фоновая операция) и выбора подходящего API (completionHandler, async/await, Combine, делегаты), является ключевым навыком для современного iOS-разработчика.