Как организовать сетевые вызовы в iOS проекте
Что проверяют
Умеете ли вы построить сетевой слой, который тестируется, переживает ошибки и не размазан по контроллерам. Следом часто спрашивают: как вы обновите токен, если пять запросов одновременно получили 401.
Как отвечать
Опишите слои, а не библиотеку:
- описание запроса: тип
Endpointс путём, методом, параметрами, телом и ожидаемым типом ответа. Всё знание об API собрано в одном месте; - клиент: протокол вроде
APIClientс методомsend<T: Decodable>(_ endpoint:) async throws -> Tи реализация наURLSessionчерезdata(for:). Протокол позволяет подменить клиент в тестах и внедрять его через инициализатор, а не синглтоном; - проверка ответа:
URLSessionне бросает ошибку на 404 или 500, статус нужно проверить самому черезHTTPURLResponse.statusCode. Ошибки сводятся в свойenum: транспорт, HTTP-статус, декодирование, отмена; - декодирование:
Codableи настроенныйJSONDecoderсо стратегиями для дат и ключей; - авторизация: добавление токена и его обновление в одном месте. Обновление держите в
actor, чтобы параллельные 401 ждали один запрос обновления, а не запускали пять; - отмена и повторы: отмена
Taskотменяет и запрос. Повторы с задержкой делайте только для идемпотентных запросов; - UI: ViewModel с
@MainActor, чтобы результат применялся на главном потоке.
Alamofire допустим, но стоит объяснить, что он даёт сверх URLSession: интерсепторы, ретраеры, валидацию статусов. Для тестов сети без сервера подходит свой подкласс URLProtocol, подключённый через protocolClasses у URLSessionConfiguration.
Где спотыкаются
Делают NetworkManager.shared и зовут его прямо из UIViewController. Такой код не тестируется, а зависимость видна только при чтении реализации.
Считают, что ответ 500 придёт в ветку ошибки. Без проверки статуса приложение пытается декодировать страницу ошибки и показывает непонятное сообщение о парсинге.
Обновляют интерфейс из completion handler без перехода на главный поток или запускают несколько обновлений токена сразу, после чего сервер отзывает выданные токены.