Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Задачи

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

AI info

Карта сайта

Документы

Медиа

Назад

Как корректно обрабатывать асинхронные сетевые запросы внутри view controller?

Вопрос проверяет понимание жизненного цикла экрана, правил обновления UI и безопасной асинхронности в UIKit.

Короткий ответ

Нужно запускать запрос так, чтобы он был связан с жизненным циклом экрана и мог быть отменен. UI обновляется только на главном потоке. Ошибки и состояния загрузки должны обрабатываться явно. Важно избегать retain cycle и обновления UI после закрытия экрана. Лучше держать сетевую логику вне контроллера, а контроллеру оставлять только отображение состояния.

Длинный ответ

Асинхронные запросы в контроллере опасны тем, что сеть живет «дольше», чем экран. Поэтому правильный подход — связать запрос с жизненным циклом и держать управление состоянием прозрачным.

1) Базовые требования к корректной реализации

Перед кодом важно зафиксировать правила, которые должны выполняться всегда.

  1. UI обновляется только на main thread.

  2. Запрос должен быть отменяемым.

  3. Результат запроса не должен применяться, если экран больше не актуален.

  4. Не должно быть утечек памяти из-за self в замыканиях или задачах.

  5. Состояния loading/success/error должны быть явными.

2) Привязка к жизненному циклу и отмена

Вариант с async/await и Task

Обычно хранят ссылку на задачу и отменяют при уходе с экрана.

final class FeedViewController: UIViewController {
    private var loadTask: Task<Void, Never>?

    override func viewDidLoad() {
        super.viewDidLoad()
        load()
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        loadTask?.cancel()
    }

    private func load() {
        setLoading(true)

        loadTask = Task { [weak self] in
            guard let self else { return }

            do {
                let items = try await self.service.fetchFeed() // service скрывает URLSession
                await MainActor.run {
                    self.setLoading(false)
                    self.render(items)
                }
            } catch {
                await MainActor.run {
                    self.setLoading(false)
                    self.showError(error)
                }
            }
        }
    }
}

Ключевые детали:

  1. loadTask хранится, чтобы отменить.

  2. Task { [weak self] ... } снижает риск retain cycle.

  3. UI обновляется внутри MainActor.run.

Почему отмена важна

Если не отменять:

  • запрос продолжится;

  • результат может прийти после pop/dismiss;

  • возможны лишние обновления, утечки, гонки состояния.

3) Защита от гонок и повторных запросов

Частая проблема: пользователь дергает pull-to-refresh, а старый запрос еще идет.

Практики:

  1. отменять предыдущую задачу перед запуском новой;

  2. хранить текущее состояние загрузки;

  3. игнорировать устаревшие ответы (например, по requestId).

Идея:

loadTask?.cancel()
loadTask = Task { ... }

4) Явное управление состояниями экрана

Контроллер должен уметь «отрисовать» состояние одним способом, а не разрозненно.

Подход:

  1. завести ScreenState;

  2. обновлять его централизованно;

  3. иметь один метод render(state:).

Даже без полного MVVM это резко снижает хаос в контроллере.

5) Где проходит граница ответственности

Правильное разделение обычно выглядит так:

  1. Контроллер:

    • запускает загрузку;

    • отображает состояния;

    • обрабатывает пользовательские действия.

  2. Сервис/репозиторий:

    • выполняет запросы;

    • декодирует данные;

    • возвращает модели или доменные ошибки.

Если декодирование и построение моделей происходит внутри контроллера — это почти всегда признак будущего Massive VC.

Практический вывод

Корректная обработка асинхронных запросов в view controller строится на трех вещах: отмена и связь с жизненным циклом, обновление UI только на main thread, явное управление состояниями (loading/success/error). Чем больше логики сети и маппинга вынесено из контроллера, тем стабильнее и поддерживаемее экран.

  • Аватар

    iOS Guru

    Roman Isakov

    Guru – это эксперты YeaHub, которые помогают развивать комьюнити.

Уровень

  • Рейтинг:

    5

  • Сложность:

    7

Навыки

  • Networks

Ключевые слова

#async

#request

Подпишись на iOS Developer в телеграм

  • Аватар

    iOS Guru

    Roman Isakov

    Guru – это эксперты YeaHub, которые помогают развивать комьюнити.