Настройка Alamofire для сетевых запросов в iOS-приложении
URLSession достаточен для простых запросов. Alamofire добавляет ценность там, где URLSession требует много boilerplate: цепочки запросов, перехватчики авторизации, retry-логика, multipart загрузка, certificate pinning. Версия 5.x построена на async/await и Combine с сохранением обратной совместимости.
Архитектура сетевого слоя с Alamofire
Главная ошибка — использовать AF.request(...) напрямую из ViewModel или ViewController. Сетевой слой должен быть изолирован.
Правильная структура: APIClient (singleton или dependency-injected) → Router (enum с URLRequestConvertible) → Alamofire Session → модели.
enum UserRouter: URLRequestConvertible {
case getProfile(id: String)
case updateProfile(UserUpdateRequest)
var method: HTTPMethod {
switch self {
case .getProfile: return .get
case .updateProfile: return .patch
}
}
func asURLRequest() throws -> URLRequest {
var request = try URLRequest(url: baseURL.appendingPathComponent(path))
request.method = method
return try encoder.encode(self, into: request)
}
}
Перехватчик авторизации (RequestInterceptor)
Автоматическое обновление access_token через refresh_token — без RequestInterceptor это десятки строк в каждом запросе:
class AuthInterceptor: RequestInterceptor {
func adapt(_ urlRequest: URLRequest, for session: Session,
completion: @escaping (Result<URLRequest, Error>) -> Void) {
var request = urlRequest
request.headers.add(.authorization(bearerToken: tokenStore.accessToken))
completion(.success(request))
}
func retry(_ request: Request, for session: Session, dueTo error: Error,
completion: @escaping (RetryResult) -> Void) {
guard request.response?.statusCode == 401 else {
completion(.doNotRetry); return
}
refreshToken { result in
switch result {
case .success: completion(.retry)
case .failure(let e): completion(.doNotRetryWithError(e))
}
}
}
}
Session с этим интерцептором автоматически добавляет токен и повторяет запрос после обновления — прозрачно для вызывающего кода.
Декодирование и обработка ошибок
responseDecodable(of:) с JSONDecoder — стандарт. Кастомный JSONDecoder с dateDecodingStrategy и keyDecodingStrategy задаём один раз в APIClient.
Серверные ошибки часто приходят в виде JSON с кодом и сообщением. ResponseSerializer кастомный или validate() + обработка в mapError:
session.request(router)
.validate(statusCode: 200..<300)
.responseDecodable(of: T.self, decoder: decoder) { response in
switch response.result {
case .success(let value): // ok
case .failure(let error):
if let data = response.data,
let apiError = try? decoder.decode(APIError.self, from: data) {
// показываем apiError.message
}
}
}
Multipart и загрузка файлов
Загрузка изображений через upload(multipartFormData:):
session.upload(multipartFormData: { formData in
formData.append(imageData, withName: "photo", fileName: "photo.jpg", mimeType: "image/jpeg")
}, with: router)
.uploadProgress { progress in
updateProgressBar(progress.fractionCompleted)
}
uploadProgress работает на main queue по умолчанию при указании .main очереди — иначе обновляем UI через DispatchQueue.main.async.
Certificate Pinning
Для приложений с чувствительными данными настраиваем certificate pinning через ServerTrustManager:
let manager = ServerTrustManager(evaluators: [
"api.example.com": PinnedCertificatesTrustEvaluator()
])
let session = Session(serverTrustManager: manager)
Сертификаты кладём в Bundle. При ротации сертификата на сервере нужно обновить приложение — без этого все запросы упадут с SSL error. Поэтому в enterprise-проектах часто используем public key pinning вместо полного сертификата.
Что входит в работу
- Настройка
Sessionс кастомнымRequestInterceptorдля авторизации -
Routerна основеURLRequestConvertibleдля всех эндпоинтов - Кастомный
JSONDecoderс нужными стратегиями - Обработка сетевых и серверных ошибок
- Загрузка файлов с прогрессом
- Опционально: certificate pinning, logging interceptor
Сроки
Базовый сетевой слой с роутером и авторизацией: 1 день. С multipart, SSL pinning, retry стратегией и полным покрытием ошибок: 2–3 дня. Стоимость рассчитывается индивидуально.







