Setting up Alamofire for network requests in iOS applications
URLSession is sufficient for simple requests. Alamofire adds value where URLSession requires significant boilerplate: request chaining, authorization interceptors, retry logic, multipart uploads, certificate pinning. Version 5.x is built on async/await and Combine while maintaining backward compatibility.
Network layer architecture with Alamofire
The main mistake is using AF.request(...) directly from ViewModel or ViewController. The network layer must be isolated.
Correct structure: APIClient (singleton or dependency-injected) → Router (enum with URLRequestConvertible) → Alamofire Session → models.
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)
}
}
Authorization interceptor (RequestInterceptor)
Automatic access_token refresh via refresh_token — without RequestInterceptor this means dozens of lines in every request:
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 with this interceptor automatically adds the token and retries the request after refresh — transparent to calling code.
Decoding and error handling
responseDecodable(of:) with JSONDecoder — standard practice. Custom JSONDecoder with dateDecodingStrategy and keyDecodingStrategy is configured once in APIClient.
Server errors often come as JSON with code and message. Custom ResponseSerializer or validate() + handling in 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) {
// show apiError.message
}
}
}
Multipart and file uploads
Upload images via 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 runs on main queue by default when .main queue is specified — otherwise update UI via DispatchQueue.main.async.
Certificate Pinning
For applications with sensitive data, configure certificate pinning via ServerTrustManager:
let manager = ServerTrustManager(evaluators: [
"api.example.com": PinnedCertificatesTrustEvaluator()
])
let session = Session(serverTrustManager: manager)
Store certificates in Bundle. When the server certificate rotates, the app must be updated — without this all requests fail with SSL error. That's why enterprise projects often use public key pinning instead of full certificate pinning.
What's included in the work
- Configuring
Sessionwith customRequestInterceptorfor authorization -
Routerbased onURLRequestConvertiblefor all endpoints - Custom
JSONDecoderwith necessary strategies - Network and server error handling
- File uploads with progress
- Optionally: certificate pinning, logging interceptor
Timelines
Basic network layer with router and authorization: 1 day. With multipart, SSL pinning, retry strategy and comprehensive error coverage: 2–3 days. Pricing calculated individually.







