Alamofire

Most popular third-party HTTP networking library for Swift. Built on URLSession while providing more user-friendly and elegant API. Chainable requests, automatic retry, response validation, JSON serialization, and Swift Concurrency support.

HTTP ClientSwiftiOSmacOSAsynchronous ProcessingAuthentication

GitHub Overview

Alamofire/Alamofire

Elegant HTTP Networking in Swift

Stars42,173
Watchers1,056
Forks7,642
Created:July 31, 2014
Language:Swift
License:MIT License

Topics

alamofirecarthagecertificate-pinningcocoapodshttpurlresponsenetworkingparameter-encodingpublic-key-pinningrequestresponseswiftswift-package-managerurlrequesturlsessionxcode

Star History

Alamofire/Alamofire Star History
Data as of: 10/22/2025, 04:10 AM

Library

Alamofire

Overview

Alamofire is a high-featured HTTP networking library for Swift developed under the concept of "Elegant HTTP Networking in Swift." As the de facto standard library in iOS and macOS development with overwhelming adoption rates, it features a design that wraps complex URLSession processing with simple and intuitive APIs. Providing comprehensive features that meet enterprise-level HTTP communication requirements including chainable methods, automatic JSON conversion, authentication systems, SSL certificate pinning, and progress monitoring, it significantly contributes to Swift developers' productivity improvements.

Details

Alamofire 2025 edition continues to evolve as the definitive HTTP library for modern iOS and macOS development with the latest Swift 6.0 support and complete Swift Concurrency support. Boasting mature APIs and exceptional stability through over 10 years of development experience, it supports a wide range of platforms from iOS 8.0+ to iOS 18.0+. In addition to a robust URLSession-based architecture, it integrates modern technologies such as chainable syntax, type-safe Codable support, automatic retry functionality, and experimental WebSocket support. Compared to Apple's standard URLSession, it achieves significant development efficiency improvements and enables flexible adoption through Swift Package Manager, CocoaPods, and Carthage.

Key Features

  • Elegant API: Chainable and readable HTTP request description
  • Complete Swift Concurrency Support: async/await and Combine compatibility
  • Comprehensive Authentication System: Basic, Digest, OAuth, and custom authentication support
  • Type-Safe Codable Support: Automatic JSON/XML conversion and error handling
  • SSL Certificate Pinning: Enterprise-level security requirements support
  • Progress Monitoring: Real-time tracking of upload and download progress

Pros and Cons

Pros

  • Overwhelming adoption rate in Swift/iOS/macOS ecosystem with abundant learning resources
  • Simple and intuitive API design that hides complex URLSession processing
  • Excellent integration with SwiftUI and Combine/async/await support
  • Active community support and regular updates
  • Enterprise-level authentication and security features with certificate pinning
  • Rich ecosystem libraries including AlamofireImage and others

Cons

  • Swift update dependency waiting due to third-party library dependence
  • Learning cost and bundle size compared to URLSession native implementation
  • Some limitations and Sendable requirement compliance issues with Swift 6.0+
  • Feature limitations and non-support in Linux, Windows, and Android environments
  • Necessity debate for simple cases due to evolution of iOS standard APIs
  • Risk of excessive abstraction in large-scale projects

Reference Pages

Code Examples

Installation and Basic Setup

// Swift Package Manager (Recommended)
// Add to Package.swift
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.10.0"))

// Import Alamofire
import Alamofire

// Version check
print("Alamofire version: \(AFInfo.version)")

// CocoaPods
// Add to Podfile
// pod 'Alamofire', '~> 5.10'

// Carthage
// Add to Cartfile
// github "Alamofire/Alamofire" ~> 5.10

Basic Requests (GET/POST/PUT/DELETE)

import Alamofire

// Basic GET request
AF.request("https://api.example.com/users").response { response in
    debugPrint(response)
}

// Type-safe JSON response processing
struct User: Codable {
    let id: Int
    let name: String
    let email: String
}

AF.request("https://api.example.com/users")
    .responseDecodable(of: [User].self) { response in
        switch response.result {
        case .success(let users):
            print("Retrieved users count: \(users.count)")
            users.forEach { user in
                print("User: \(user.name) - \(user.email)")
            }
        case .failure(let error):
            print("Error: \(error)")
        }
    }

// GET request with query parameters
let parameters: [String: Any] = [
    "page": 1,
    "limit": 20,
    "sort": "created_at",
    "order": "desc"
]

AF.request("https://api.example.com/users", parameters: parameters)
    .responseDecodable(of: [User].self) { response in
        debugPrint(response)
    }

// POST request (sending JSON)
let newUser = [
    "name": "John Doe",
    "email": "[email protected]",
    "age": 30
] as [String: Any]

AF.request("https://api.example.com/users",
           method: .post,
           parameters: newUser,
           encoding: JSONEncoding.default)
    .responseDecodable(of: User.self) { response in
        switch response.result {
        case .success(let createdUser):
            print("User creation successful: \(createdUser.name)")
        case .failure(let error):
            print("Creation error: \(error)")
        }
    }

// PUT request (data update)
let updateData = ["name": "Jane Doe", "email": "[email protected]"]

AF.request("https://api.example.com/users/123",
           method: .put,
           parameters: updateData,
           encoding: JSONEncoding.default)
    .responseDecodable(of: User.self) { response in
        debugPrint(response)
    }

// DELETE request
AF.request("https://api.example.com/users/123", method: .delete)
    .response { response in
        if response.response?.statusCode == 204 {
            print("User deletion completed")
        } else {
            print("Deletion error: \(response.error?.localizedDescription ?? "Unknown error")")
        }
    }

// Detailed response inspection
AF.request("https://api.example.com/users").response { response in
    print("Status code: \(response.response?.statusCode ?? 0)")
    print("Headers: \(response.response?.allHeaderFields ?? [:])")
    print("Data size: \(response.data?.count ?? 0) bytes")
    print("Execution time: \(response.metrics?.taskInterval.duration ?? 0) seconds")
}

Advanced Configuration and Customization (Headers, Authentication, Validation, etc.)

import Alamofire

// Custom header configuration
let headers: HTTPHeaders = [
    "Authorization": "Bearer your-jwt-token",
    "Accept": "application/json",
    "Content-Type": "application/json",
    "User-Agent": "MyiOSApp/1.0",
    "X-API-Version": "v2"
]

AF.request("https://api.example.com/protected-data", headers: headers)
    .responseDecodable(of: [String: Any].self) { response in
        debugPrint(response)
    }

// Type-safe header configuration with HTTPHeaders
let typedHeaders: HTTPHeaders = [
    .authorization(bearerToken: "your-jwt-token"),
    .accept("application/json"),
    .contentType("application/json"),
    .userAgent("MyiOSApp/1.0")
]

// Basic authentication
AF.request("https://api.example.com/basic-auth")
    .authenticate(username: "user", password: "password")
    .responseDecodable(of: [String: Any].self) { response in
        debugPrint(response)
    }

// Authentication with URLCredential
let credential = URLCredential(user: "user", password: "password", persistence: .forSession)
AF.request("https://api.example.com/secure")
    .authenticate(with: credential)
    .responseDecodable(of: [String: Any].self) { response in
        debugPrint(response)
    }

// Response validation
AF.request("https://api.example.com/data")
    .validate(statusCode: 200..<300)
    .validate(contentType: ["application/json"])
    .responseDecodable(of: [String: Any].self) { response in
        switch response.result {
        case .success(let data):
            print("Validation successful: \(data)")
        case .failure(let error):
            print("Validation error: \(error)")
        }
    }

// Custom validation
AF.request("https://api.example.com/data")
    .validate { request, response, data in
        // Custom validation logic
        if response.statusCode == 200 {
            return .success(())
        } else {
            let error = AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: response.statusCode))
            return .failure(error)
        }
    }
    .responseDecodable(of: [String: Any].self) { response in
        debugPrint(response)
    }

// URLRequestModifier usage example
AF.request("https://api.example.com/data") { urlRequest in
    urlRequest.timeoutInterval = 30
    urlRequest.cachePolicy = .reloadIgnoringCacheData
    urlRequest.setValue("en-US", forHTTPHeaderField: "Accept-Language")
}
.responseDecodable(of: [String: Any].self) { response in
    debugPrint(response)
}

// SSL certificate pinning (using ServerTrustManager)
let evaluators: [String: ServerTrustEvaluating] = [
    "api.example.com": PinnedCertificatesTrustEvaluator(),
    "secure.example.com": PublicKeysTrustEvaluator()
]

let manager = ServerTrustManager(evaluators: evaluators)
let session = Session(serverTrustManager: manager)

session.request("https://api.example.com/secure-data")
    .responseDecodable(of: [String: Any].self) { response in
        debugPrint(response)
    }

Error Handling and Retry Functionality

import Alamofire

// Comprehensive error handling
func performSafeRequest() {
    AF.request("https://api.example.com/data")
        .validate()
        .responseDecodable(of: [String: Any].self) { response in
            switch response.result {
            case .success(let data):
                print("Success: \(data)")
                
            case .failure(let error):
                // Detailed Alamofire error processing
                if let afError = error.asAFError {
                    switch afError {
                    case .responseValidationFailed(let reason):
                        switch reason {
                        case .unacceptableStatusCode(let code):
                            print("Invalid status code: \(code)")
                        case .unacceptableContentType(let acceptableTypes, let responseType):
                            print("Invalid Content-Type: \(responseType), expected: \(acceptableTypes)")
                        default:
                            print("Response validation error: \(reason)")
                        }
                        
                    case .sessionTaskFailed(let sessionError):
                        if let urlError = sessionError as? URLError {
                            switch urlError.code {
                            case .notConnectedToInternet:
                                print("No internet connection")
                            case .timedOut:
                                print("Request timed out")
                            case .cannotFindHost:
                                print("Cannot find host")
                            default:
                                print("Network error: \(urlError.localizedDescription)")
                            }
                        }
                        
                    case .responseSerializationFailed(let reason):
                        print("Response serialization error: \(reason)")
                        
                    default:
                        print("Alamofire error: \(afError)")
                    }
                } else {
                    print("Other error: \(error)")
                }
            }
        }
}

// Automatic retry functionality implementation
let retryPolicy = RetryPolicy(
    retryLimit: 3,
    exponentialBackoffBase: 2,
    exponentialBackoffScale: 1.0,
    retryableHTTPMethods: [.get, .post, .put, .delete],
    retryableHTTPStatusCodes: [408, 429, 500, 502, 503, 504],
    retryableURLErrorCodes: [
        .timedOut, .cannotConnectToHost, .networkConnectionLost,
        .dnsLookupFailed, .cannotFindHost
    ]
)

AF.request("https://api.example.com/unreliable", interceptor: retryPolicy)
    .responseDecodable(of: [String: Any].self) { response in
        switch response.result {
        case .success(let data):
            print("Retry successful: \(data)")
        case .failure(let error):
            print("Finally failed: \(error)")
        }
    }

// Custom RequestInterceptor implementation
class CustomRequestInterceptor: RequestInterceptor {
    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        var urlRequest = urlRequest
        urlRequest.setValue("Bearer \(getAuthToken())", forHTTPHeaderField: "Authorization")
        completion(.success(urlRequest))
    }
    
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        guard request.retryCount < 3 else {
            completion(.doNotRetry)
            return
        }
        
        if let response = request.task?.response as? HTTPURLResponse,
           response.statusCode == 401 {
            // For authentication errors, refresh token and retry
            refreshAuthToken { success in
                if success {
                    completion(.retry)
                } else {
                    completion(.doNotRetry)
                }
            }
        } else if let afError = error.asAFError,
                  case .sessionTaskFailed(let urlError as URLError) = afError,
                  [.timedOut, .cannotConnectToHost].contains(urlError.code) {
            // For network errors, retry with exponential backoff
            let delay = pow(2.0, Double(request.retryCount))
            completion(.retryWithDelay(delay))
        } else {
            completion(.doNotRetry)
        }
    }
    
    private func getAuthToken() -> String {
        // Authentication token retrieval logic
        return "current-auth-token"
    }
    
    private func refreshAuthToken(completion: @escaping (Bool) -> Void) {
        // Token refresh logic
        completion(true)
    }
}

// Using custom interceptor
let interceptor = CustomRequestInterceptor()
AF.request("https://api.example.com/protected", interceptor: interceptor)
    .responseDecodable(of: [String: Any].self) { response in
        debugPrint(response)
    }

File Upload and Download

import Alamofire
import UIKit

// Data upload
let imageData = UIImage(named: "sample-image")?.jpegData(compressionQuality: 0.8)

AF.upload(imageData!, to: "https://api.example.com/upload")
    .uploadProgress { progress in
        print("Upload progress: \(progress.fractionCompleted * 100)%")
    }
    .responseDecodable(of: [String: Any].self) { response in
        switch response.result {
        case .success(let result):
            print("Upload successful: \(result)")
        case .failure(let error):
            print("Upload error: \(error)")
        }
    }

// File upload
let fileURL = Bundle.main.url(forResource: "document", withExtension: "pdf")!

AF.upload(fileURL, to: "https://api.example.com/upload-file")
    .uploadProgress(queue: .main) { progress in
        DispatchQueue.main.async {
            // UI progress update
            print("File upload: \(Int(progress.fractionCompleted * 100))%")
        }
    }
    .responseDecodable(of: [String: Any].self) { response in
        debugPrint(response)
    }

// Multipart form data upload
AF.upload(multipartFormData: { multipartFormData in
    // Text data
    multipartFormData.append("John Doe".data(using: .utf8)!, withName: "name")
    multipartFormData.append("[email protected]".data(using: .utf8)!, withName: "email")
    
    // File data
    if let imageData = UIImage(named: "profile")?.jpegData(compressionQuality: 0.8) {
        multipartFormData.append(imageData, withName: "avatar", fileName: "profile.jpg", mimeType: "image/jpeg")
    }
    
    // Document file
    if let documentURL = Bundle.main.url(forResource: "resume", withExtension: "pdf") {
        multipartFormData.append(documentURL, withName: "resume", fileName: "resume.pdf", mimeType: "application/pdf")
    }
    
}, to: "https://api.example.com/profile") { result in
    switch result {
    case .success(let upload, _, _):
        upload.uploadProgress { progress in
            print("Multipart progress: \(progress.fractionCompleted * 100)%")
        }
        upload.responseDecodable(of: [String: Any].self) { response in
            debugPrint(response)
        }
    case .failure(let error):
        print("Multipart error: \(error)")
    }
}

// File download
let destination: DownloadRequest.Destination = { _, _ in
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fileURL = documentsURL.appendingPathComponent("downloaded-file.zip")
    return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}

AF.download("https://api.example.com/large-file.zip", to: destination)
    .downloadProgress(queue: .main) { progress in
        print("Download progress: \(Int(progress.fractionCompleted * 100))%")
        print("Downloaded: \(ByteCountFormatter.string(fromByteCount: progress.completedUnitCount, countStyle: .file))")
        print("Total size: \(ByteCountFormatter.string(fromByteCount: progress.totalUnitCount, countStyle: .file))")
    }
    .response { response in
        if response.error == nil, let filePath = response.fileURL?.path {
            print("Download completed: \(filePath)")
        } else {
            print("Download error: \(response.error?.localizedDescription ?? "Unknown error")")
        }
    }

// Download pause and resume
var downloadRequest: DownloadRequest?

downloadRequest = AF.download("https://api.example.com/large-file.zip")
    .downloadProgress { progress in
        print("Progress: \(progress.fractionCompleted)")
    }
    .response { response in
        print("Download completed or interrupted")
    }

// Pause and get resume data
downloadRequest?.cancel { resumeData in
    if let resumeData = resumeData {
        // Resume using resume data
        AF.download(resumingWith: resumeData)
            .downloadProgress { progress in
                print("Resume progress: \(progress.fractionCompleted)")
            }
            .response { response in
                print("Resume download completed")
            }
    }
}

Swift Concurrency (async/await) Support

import Alamofire

// Basic request using async/await
func fetchUsersAsync() async throws -> [User] {
    let response = await AF.request("https://api.example.com/users")
        .validate()
        .serializingDecodable([User].self)
        .response
    
    switch response.result {
    case .success(let users):
        return users
    case .failure(let error):
        throw error
    }
}

// Usage example
Task {
    do {
        let users = try await fetchUsersAsync()
        print("Retrieved users: \(users.count)")
    } catch {
        print("Error: \(error)")
    }
}

// Parallel processing of multiple asynchronous requests
func fetchMultipleDataAsync() async throws -> (users: [User], posts: [Post]) {
    async let usersResponse = AF.request("https://api.example.com/users")
        .validate()
        .serializingDecodable([User].self)
        .response
    
    async let postsResponse = AF.request("https://api.example.com/posts")
        .validate()
        .serializingDecodable([Post].self)
        .response
    
    let (usersResult, postsResult) = await (usersResponse, postsResponse)
    
    guard case .success(let users) = usersResult.result,
          case .success(let posts) = postsResult.result else {
        throw AFError.responseSerializationFailed(reason: .inputDataNil)
    }
    
    return (users: users, posts: posts)
}

// async/await upload
func uploadImageAsync(_ imageData: Data) async throws -> [String: Any] {
    let response = await AF.upload(imageData, to: "https://api.example.com/upload")
        .validate()
        .serializingDecodable([String: Any].self)
        .response
    
    switch response.result {
    case .success(let result):
        return result
    case .failure(let error):
        throw error
    }
}

// Parallel upload of multiple files using TaskGroup
func uploadMultipleFilesAsync(_ files: [Data]) async throws -> [[String: Any]] {
    return try await withThrowingTaskGroup(of: [String: Any].self) { group in
        for (index, fileData) in files.enumerated() {
            group.addTask {
                return try await self.uploadImageAsync(fileData)
            }
        }
        
        var results: [[String: Any]] = []
        for try await result in group {
            results.append(result)
        }
        return results
    }
}

// Streaming using AsyncSequence (experimental)
func streamDataAsync() async throws {
    let request = AF.streamRequest("https://api.example.com/stream")
    
    for try await data in request.asAsyncSequence() {
        // Process streaming data
        print("Received data: \(data.count) bytes")
    }
}

Combine Integration

import Alamofire
import Combine

// Basic request using Combine
func fetchUsersPublisher() -> AnyPublisher<[User], AFError> {
    return AF.request("https://api.example.com/users")
        .validate()
        .publishDecodable(type: [User].self)
        .value()
        .eraseToAnyPublisher()
}

// Usage example
var cancellables = Set<AnyCancellable>()

fetchUsersPublisher()
    .receive(on: DispatchQueue.main)
    .sink(
        receiveCompletion: { completion in
            switch completion {
            case .finished:
                print("Request completed")
            case .failure(let error):
                print("Error: \(error)")
            }
        },
        receiveValue: { users in
            print("Users retrieved: \(users.count)")
        }
    )
    .store(in: &cancellables)

// Combining multiple requests
Publishers.Zip(
    AF.request("https://api.example.com/users").publishDecodable(type: [User].self).value(),
    AF.request("https://api.example.com/posts").publishDecodable(type: [Post].self).value()
)
.receive(on: DispatchQueue.main)
.sink(
    receiveCompletion: { completion in
        print("Combined request completion: \(completion)")
    },
    receiveValue: { users, posts in
        print("Data retrieved - Users: \(users.count), Posts: \(posts.count)")
    }
)
.store(in: &cancellables)

// Chain requests (fetch users → fetch details)
fetchUsersPublisher()
    .flatMap { users -> AnyPublisher<[UserDetail], AFError> in
        let userIds = users.map { $0.id }
        return AF.request("https://api.example.com/users/details", 
                         parameters: ["ids": userIds])
            .publishDecodable(type: [UserDetail].self)
            .value()
            .eraseToAnyPublisher()
    }
    .receive(on: DispatchQueue.main)
    .sink(
        receiveCompletion: { completion in
            print("Chain request completion: \(completion)")
        },
        receiveValue: { userDetails in
            print("Detail data retrieved: \(userDetails.count) items")
        }
    )
    .store(in: &cancellables)