Настройка URLSession для iOS: аудит, оптимизация, избегаем утечек

Как настроить URLSession для продакшена и избежать утечек URLSession — стандартный сетевой стек Apple, но в продакшене дефолтная конфигурация часто подводит. `URLSession.shared` не поддерживает background-загрузки, не позволяет настроить таймауты на уровне сессии и не даёт доступа к делегату для

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Настройка URLSession для iOS: аудит, оптимизация, избегаем утечек
Средний
от 1 дня до 3 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    597

Как настроить URLSession для продакшена и избежать утечек

URLSession — стандартный сетевой стек Apple, но в продакшене дефолтная конфигурация часто подводит. URLSession.shared не поддерживает background-загрузки, не позволяет настроить таймауты на уровне сессии и не даёт доступа к делегату для SSL pinning. Мы сталкивались с этим десятки раз: утечки памяти до 15 МБ после 50 запросов, нарушение политики App Transport Security, сложно диагностируемые таймауты. Наш подход, основанный на протокольной абстракции и async/await, позволяет избежать 90% типовых ошибок и сократить время на отладку на 40%. Это экономия до 20% бюджета разработки. Закажите аудит текущего сетевого слоя — получите конкретные рекомендации за 1 день.

Где чаще всего ошибаются при настройке URLSession?

Неправильный URLSessionConfiguration. .shared не поддерживает background-transfer и не даёт настроить таймауты на уровне сессии. Для API-клиента нужно минимум:

let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 15 config.timeoutIntervalForResource = 60 config.requestCachePolicy = .reloadIgnoringLocalCacheData config.urlCache = nil let session = URLSession(configuration: config, delegate: self, delegateQueue: nil) 

delegateQueue: nil означает, что URLSession создаст свою serial-очередь. Если передать OperationQueue.main — все completion handlers будут выполняться на main thread, что заблокирует UI при медленном разборе JSON.

Игнорирование URLSessionTaskDelegate при работе с SSL pinning — вторая по частоте ошибка. Без делегата невозможно переопределить urlSession(_:didReceive:completionHandler:) для проверки сертификата. Сколько приложений мы видели, где SSL-pinning реализован через стороннюю библиотеку, но фактически не работает, потому что сессия создана без делегата — не счесть. В наших проектах мы всегда используем делегат и проверяем отпечаток сертификата вручную.

Как избежать утечек памяти?

Утечки через [weak self] — классика. URLSessionDataTask удерживает strong-ссылку на делегат сессии до явного вызова session.invalidateAndCancel() или finishTasksAndInvalidate(). Если URLSession хранится как свойство класса, а сам класс при deinit не инвалидирует сессию — цикл сохраняется. В одном из проектов мы обнаружили утечку 15 МБ после 50 запросов — инвалидация сессии решила проблему. Всегда используйте [weak self] в замыканиях и инвалидируйте сессию в deinit.

Как мы строим сетевой слой с async/await?

Основа — Protocol-Oriented подход с NetworkClient протоколом, что позволяет мокировать запросы в Unit-тестах без необходимости поднимать сервер.

protocol NetworkClient { func send<T: Decodable>(_ request: URLRequest) async throws -> T } final class URLSessionNetworkClient: NetworkClient { private let session: URLSession private let decoder: JSONDecoder init(session: URLSession = .init(configuration: .default)) { self.session = session self.decoder = JSONDecoder() self.decoder.keyDecodingStrategy = .convertFromSnakeCase self.decoder.dateDecodingStrategy = .iso8601 } func send<T: Decodable>(_ request: URLRequest) async throws -> T { let (data, response) = try await session.data(for: request) guard let http = response as? HTTPURLResponse else { throw NetworkError.invalidResponse } guard (200..<300).contains(http.statusCode) else { throw NetworkError.httpError(statusCode: http.statusCode, data: data) } return try decoder.decode(T.self, from: data) } } 

Async/await вместо completion handlers — это не просто синтаксический сахар. Structured concurrency позволяет отменять запросы через Task.cancel(), который автоматически вызывает task.cancel() на уровне URLSession. Старая схема с completion-блоками не давала такого контроля.

Retry-логика реализуется через обёртку, а не засорение основного клиента:

func sendWithRetry<T: Decodable>( _ request: URLRequest, maxAttempts: Int = 3, delay: Duration = .seconds(1) ) async throws -> T { var lastError: Error for attempt in 0..<maxAttempts { do { return try await send(request) } catch NetworkError.httpError(let code, _) where code >= 500 { lastError = NetworkError.httpError(statusCode: code, data: nil) if attempt < maxAttempts - 1 { try await Task.sleep(for: delay * Double(attempt + 1)) } } catch { throw error // не ретраим 4xx и ошибки декодирования } } throw lastError } 

Background Downloads — для загрузки файлов используйте URLSessionConfiguration.background(withIdentifier:). Система может завершить процесс и возобновить загрузку при следующем запуске. Обязательный метод в AppDelegate:

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) { BackgroundDownloadManager.shared.completionHandler = completionHandler } 

Без этого обработчика iOS не перезапустит приложение после завершения фоновой загрузки.

Почему самописный NetworkClient лучше готовых библиотек?

Критерий Собственный NetworkClient Alamofire / Moya
Размер фреймворка 0 КБ ~1-2 МБ
Контроль над кодом Полный Ограниченный
Зависимость от версий Нет Обновления могут ломать
Отладка Проще (свой код) Сложнее (чёрный ящик)
Unit-тесты Моки через протокол Требует дополнительных врапперов

В 90% случаев самописный клиент покрывает все потребности и не тянет лишние зависимости. Мы используем внешние библиотеки только если нужна сложная кэш-политика или WebSocket.

Детальный разбор ошибки: игнорирование делегата SSL

Рассмотрим типовой сценарий: разработчик добавляет Alamofire с ServerTrustEvaluator, но забывает, что сам создаёт сессию без делегата. В результате SSL pinning не работает, и приложение уязвимо к MITM-атакам. В одном проекте мы обнаружили, что из-за этого данные банковской карты передавались через незащищённое соединение. После замены на собственный NetworkClient с ручной проверкой сертификата проблема исчезла. Экономия на исправлении уязвимости — около $10 000 в потенциальных потерях. Всегда проверяйте, что делегат установлен.

Диагностика проблем

Инструменты: Charles Proxy или Proxyman для инспекции трафика; Network Instrument в Xcode для анализа количества параллельных соединений и поиска connection starvation; os_log с категорией com.apple.network для низкоуровневого логирования Network.framework.

При ошибке NSURLErrorDomain -1001 (request timed out) первым делом смотреть на timeoutIntervalForRequest — по умолчанию 60 секунд, что неочевидно много для мобильного приложения. При NSURLErrorDomain -1200 (SSL error) — проверить ATS-политику в Info.plist и корректность цепочки сертификатов на сервере через openssl s_client. В одном из проектов мы уменьшили время ответа на 40% просто заменив кэш-политику на .reloadIgnoringLocalCacheData.

Процесс работы

  1. Аудит существующего сетевого слоя: проверка конфигурации сессии, обработки ошибок, таймаутов, работы с токенами авторизации.
  2. Проектирование: API-клиент с поддержкой авторизации через URLSessionTaskDelegate или RequestInterceptor, обработка 401 с обновлением токена.
  3. Разработка: реализация, покрытие Unit-тестами через mock-сессию (URLProtocol subclass).
  4. Тестирование: интеграционные тесты на реальном API, проверка поведения при нестабильной сети через Network Link Conditioner.

Что входит в нашу работу

  • Аудит текущей реализации URLSession и выявление утечек.
  • Проектирование и реализация NetworkClient с учётом ваших требований.
  • Настройка SSL pinning с привязкой к сертификату или отпечатку.
  • Добавление retry-логики с exponential backoff.
  • Интеграция обновления токенов при 401.
  • Покрытие Unit-тестами (100% основных сценариев).
  • Документация кода и инструкция по эксплуатации.
  • Поддержка в течение месяца после сдачи.

Ориентиры по срокам

Задача Срок
Базовый API-клиент с async/await 1 день
+ SSL pinning + retry + token refresh 2–3 дня
Миграция существующего сетевого слоя 2–3 дня

Почему стоит доверить настройку URLSession профессионалам?

Мы занимаемся iOS-разработкой более 5 лет и реализовали сетевые слои в 30+ приложениях — от стартапов до enterprise-решений. Знаем все подводные камни URLSession: от ATS-исключений до правильной обработки Server-Sent Events. Гарантируем, что после нашей настройки ваше приложение не будет падать на нестабильной сети и сможет работать в условиях плохого соединения.

Закажите аудит текущего сетевого слоя или разработку нового — оценим ваш проект за 1 день. Получите консультацию — мы подберём оптимальное решение. Дополнительно ознакомьтесь с URLSession Programming Guide от Apple.