Первая часть, про архитектуру приложения.
Разработка сетевого слоя
Вопрос 6. Как бы вы спроектировали переиспользуемый сетевой слой?
Почему интервьюеры задают этот вопрос
Интервьюеры хотят понять, умеете ли вы строить сетевой код, который работает во всём приложении, а не писать отдельные запросы к API для каждого экрана.
Хороший сетевой слой должен быть переиспользуемым, тестируемым и легко расширяемым.
Ответ уровня старшего разработчика
Обычно я разделяю сетевую работу на несколько переиспользуемых компонентов.
ViewModel
↓
Repository
↓
API Client
↓
URLSession
ViewModel никогда не взаимодействует напрямую с URLSession.
Вместо этого она запрашивает данные у репозитория, а тот передаёт выполнение запроса централизованному API клиенту.
API Client отвечает за:
- Создание запросов
- Отправку запросов
- Декодирование ответов
- Обработку общих ошибок
- Логирование запросов, если требуется
Такой подход позволяет избежать дублирования сетевой логики по всему проекту.
Пример из реального проекта
Представим, что в приложении есть:
- Логин
- Дашборд
- Заказы
- Платежи
- Профиль.
Если каждая ViewModel самостоятельно создаёт запросы через URLSession, со временем вам придётся поддерживать одну и ту же сетевую логику в разных местах. Вместо этого каждая функция использует общий API Client, что упрощает добавление заголовков авторизации, логирования, повторных попыток или перехватчиков запросов.
Распространённая ошибка
Распространённая ошибка — размещать сетевой код прямо внутри ViewModel.
На ранних этапах это может работать, но по мере роста проекта такой код становится сложнее тестировать и поддерживать.
Сетевая логика должна находиться в отдельных сервисах или репозиториях.
Дополнительные вопросы
- Почему стоит использовать URLSession, а не сторонние библиотеки?
- Как бы вы покрыли это модульными тестами?
- Где должно находиться логирование запросов?
- Как поддержать несколько окружений?
Вопрос 7. Как реализовать автоматическое обновление токена?
Почему интервьюеры задают этот вопрос
Авторизация — частое требование в рабочих приложениях. Интервьюеры хотят понять, как вы обрабатываете истёкшие токены доступа, не заставляя пользователя постоянно входить заново.
Ответ уровня старшего разработчика
Когда сервер возвращает ответ 401 Unauthorized, приложение должно попытаться автоматически обновить токен доступа.
Типичный сценарий выглядит так:
API Request ↓ 401 Unauthorized ↓ Refresh Token API ↓ New Access Token ↓ Retry Original Request
Пользователь не должен замечать этот процесс.
Если обновление токена прошло успешно, нужно повторить неудавшийся запрос.
Если обновление не удалось, пользователя следует перенаправить на экран входа.
Пример из реального проекта
Представим банковское приложение, которое открыто несколько часов. Токен доступа истекает через один час. Вместо того чтобы показывать сообщение об ошибке, приложение незаметно обновляет токен и продолжает выполнять запрос.
Для пользователя всё выглядит бесшовно.
Распространённая ошибка
Некоторые разработчики обновляют токен отдельно для каждого неудачного запроса.
Если пять запросов одновременно завершились ошибкой, приложение может отправить пять запросов на обновление токена.
Лучшее решение — обновить токен один раз, а остальные запросы поставить в очередь до получения нового токена.
Дополнительные вопросы
- Где хранить обновленные токены?
- Что происходит, если обновление токена не удалось?
- Как избежать бесконечных циклов повторных запросов?
- Как синхронизировать несколько запросов?
Вопрос 8. Как повторять неудачные запросы к API?
Почему интервьюеры задают этот вопрос
Сетевые сбои происходят постоянно. Интервьюеры хотят понять, знаете ли вы, когда повторные попытки уместны, а когда нет.
Ответ уровня старшего разработчика
Не каждую ошибку следует обрабатывать повторной попыткой.
Например, можно повторять, если есть:
- Временные проблемы с сетью
- Превышение времени ожидания на сервере
- 503 Service Unavailable
Не следует повторять, если:
- 400 Bad Request
- 401 Unauthorized
- Запрос, для которого нужно обновлять токен
- 404 Not Found
- Ошибки валидации
Я также не повторяю запросы мгновенно.
Вместо этого я использую экспоненциальную задержку.
Attempt 1 ↓ Wait 1 second ↓ Attempt 2 ↓ Wait 2 seconds ↓ Attempt 3 ↓ Wait 4 seconds
Это снижает лишнюю нагрузку на серверные службы.
Пример из реального проекта
Предположим, сервер временно недоступен во время развёртывания.
Повтор запроса с экспоненциальной задержкой часто позволяет успешно выполнить операцию без участия пользователя.
Распространённая ошибка
Повторять каждый запрос бесконечно. Это расходует заряд аккумулятора, потребляет сетевой трафик и может перегрузить серверы.
Всегда нужно задавать максимальное количество повторных попыток.
Дополнительные вопросы
- Сколько повторных попыток вы бы разрешили?
- Нужно ли повторять загрузку изображений?
- Стоит ли автоматически повторять платёжные запросы?
- Как уведомлять пользователей?
Вопрос 9. Как отменять запросы к API?
Почему интервьюеры задают этот вопрос
Пользователи постоянно переходят между экранами.
Продолжение ненужных сетевых запросов расходует трафик, заряд аккумулятора и ресурсы устройства.
Интервьюеры хотят понять, умеет ли ваш сетевой слой корректно обрабатывать отмену запросов.
Ответ уровня старшего разработчика
Когда экран исчезает или запрошенные данные больше не нужны, я отменяю связанный сетевой запрос.
Примеры:
- Пользователь закрыл экран
- Пользователь начал новый поиск
- Пользователь вышел из аккаунта
- Запрос стал неактуален
Со Swift Concurrency отмена стала намного проще, потому что Task поддерживает кооперативную отмену.
Это помогает уменьшить лишнюю работу и улучшает общую производительность.
Пример из реального проекта
Представим экран поиска. Пользователь вводит:
A Ap App Appl Apple
Без отмены будет отправлено пять запросов. При корректной отмене завершится только последний запрос. Приложение будет ощущаться быстрее и при этом использовать меньше ресурсов.
Распространённая ошибка
Полностью игнорировать отмену. Во многих приложениях большие ответы продолжают загружаться даже после того, как пользователь покинул экран.
Дополнительные вопросы
- Когда запросы не следует отменять?
- Должны ли загрузки файлов на сервер продолжаться в фоне?
- Чем фоновые скачивания отличаются от обычных?
- Как асинхронные Task обрабатывают отмену?
Вопрос 10. Как предотвратить дублирующиеся запросы к API?
Почему интервьюеры задают этот вопрос
Дублирующиеся запросы — одна из самых распространённых проблем в рабочих приложениях. Они расходуют трафик, увеличивают нагрузку на сервер и часто создают несогласованные состояния интерфейса.
Ответ уровня старшего разработчика
Сначала я выясняю, почему возникают дубли.
Типичные причины:
- Многократные нажатия на кнопку
- Несколько наблюдателей
- Повторные события жизненного цикла
- Быстрая прокрутка
- Состояния гонки
В зависимости от ситуации я могу:
- Отключить кнопку на время загрузки
- Кэшировать уже выполняющиеся запросы
- Применять задержку ввода
- Ограничивать частоту повторных действий
- Переиспользовать существующие запросы вместо создания новых
Решение зависит от конкретной функции. Здесь не стоит применять одно универсальное правило ко всему приложению.
Пример из реального проекта
Предположим, пользователь пять раз нажимает кнопку входа.
Без защиты:
Tap × 5 ↓ Five Login Requests ↓ Possible duplicate sessions
Лучший подход:
First Tap ↓ Start Request ↓ Disable Button ↓ Enable Button After Response
В результате обрабатывается только один запрос. Пользовательский опыт становится чище, а нагрузка на сервер снижается.
Распространённая ошибка
Разработчики часто обвиняют бэкенд в дублирующихся запросах.
На практике проблема нередко возникает на клиенте из-за повторных событий жизненного цикла, отсутствующих состояний загрузки или неправильного управления состоянием.
Всегда сначала исследуйте клиентскую часть, прежде чем считать, что виноват сервер.
Дополнительные вопросы
- Как реализовать задержку ввода для строки поиска?
- В чём разница между debounce и throttle?
- Как обнаруживать дублирующиеся запросы?
- Стоит ли кэшировать одинаковые GET-запросы?
Заключение
Сетевой слой — это гораздо больше, чем просто выполнение запросов к API. Старшие iOS-разработчики проектируют сетевой код так, чтобы он был переиспользуемым, безопасным, тестируемым и устойчивым к реальным сбоям.
На собеседовании не ограничивайтесь объяснением того, что именно вы бы построили. Объясняйте, почему приняли каждое архитектурное решение, и обсуждайте связанные с ним компромиссы. Интервьюеров часто интересует не один «правильный» ответ, а ход ваших рассуждений.
В третьей части мы выйдем за рамки сетевого слоя и перейдём к локальному хранению данных, архитектуре offline-first, Core Data, стратегиям синхронизации и разрешению конфликтов — темам, которые часто встречаются на собеседованиях по системному проектированию для старших iOS-разработчиков.

