Cookie management is essential for web scraping with Alamofire, especially when dealing with session-based authentication, login flows, or maintaining state across multiple requests. This guide covers comprehensive cookie handling techniques in Swift.
Understanding Cookie Management in Alamofire
Alamofire automatically handles cookies through iOS's HTTPCookieStorage
system. By default, cookies are stored and sent automatically, but you often need manual control for web scraping scenarios.
Automatic Cookie Handling
import Alamofire
// Alamofire automatically handles cookies for subsequent requests
AF.request("https://example.com/login", method: .post, parameters: [
"username": "user@example.com",
"password": "password"
]).response { response in
// Session cookies are automatically stored
print("Login response: \(response)")
}
// Cookies from login are automatically included in this request
AF.request("https://example.com/protected-page").response { response in
print("Protected page accessed with stored cookies")
}
Manual Cookie Management
Creating and Setting Cookies
import Alamofire
import Foundation
// Create a session cookie
func createSessionCookie() {
let cookieProperties: [HTTPCookiePropertyKey: Any] = [
.domain: "example.com",
.path: "/",
.name: "sessionId",
.value: "abc123xyz789",
.secure: "FALSE", // Use "TRUE" for HTTPS only
.httpOnly: "TRUE", // Prevents JavaScript access
.expires: Date(timeIntervalSinceNow: 3600) // 1 hour from now
]
if let cookie = HTTPCookie(properties: cookieProperties) {
HTTPCookieStorage.shared.setCookie(cookie)
print("Cookie set: \(cookie.name) = \(cookie.value)")
}
}
// Create an authentication token cookie
func setAuthCookie(token: String) {
let cookieProperties: [HTTPCookiePropertyKey: Any] = [
.domain: ".example.com", // Dot prefix for subdomain access
.path: "/",
.name: "auth_token",
.value: token,
.secure: "TRUE",
.httpOnly: "TRUE"
]
if let cookie = HTTPCookie(properties: cookieProperties) {
HTTPCookieStorage.shared.setCookie(cookie)
}
}
Retrieving Cookies
// Get all cookies for a specific URL
func getCookiesForURL(_ urlString: String) -> [HTTPCookie] {
guard let url = URL(string: urlString) else { return [] }
return HTTPCookieStorage.shared.cookies(for: url) ?? []
}
// Get specific cookie by name
func getCookie(named cookieName: String, for urlString: String) -> HTTPCookie? {
let cookies = getCookiesForURL(urlString)
return cookies.first { $0.name == cookieName }
}
// Print all cookies for debugging
func printCookies(for urlString: String) {
let cookies = getCookiesForURL(urlString)
print("Cookies for \(urlString):")
for cookie in cookies {
print(" \(cookie.name) = \(cookie.value)")
print(" Domain: \(cookie.domain), Path: \(cookie.path)")
print(" Secure: \(cookie.isSecure), HttpOnly: \(cookie.isHTTPOnly)")
if let expires = cookie.expiresDate {
print(" Expires: \(expires)")
}
}
}
Manual Cookie Headers
// Set cookies manually in request headers
func requestWithManualCookies() {
let cookieString = "sessionId=abc123; auth_token=xyz789; preferences=dark_mode"
let headers: HTTPHeaders = [
"Cookie": cookieString,
"User-Agent": "MyApp/1.0"
]
AF.request("https://example.com/api/data", headers: headers)
.responseJSON { response in
print("Response with manual cookies: \(response)")
}
}
// Build cookie string from HTTPCookie objects
func buildCookieString(from cookies: [HTTPCookie]) -> String {
return cookies.map { "\($0.name)=\($0.value)" }.joined(separator: "; ")
}
Custom Session Configuration
Creating Isolated Cookie Storage
// Create a custom session with isolated cookie storage
func createCustomSession() -> Session {
let configuration = URLSessionConfiguration.default
// Create custom cookie storage
let cookieStorage = HTTPCookieStorage()
cookieStorage.cookieAcceptPolicy = .always
configuration.httpCookieStorage = cookieStorage
// Configure cookie acceptance
configuration.httpCookieAcceptPolicy = .always
configuration.httpShouldSetCookies = true
return Session(configuration: configuration)
}
// Usage example
let customSession = createCustomSession()
customSession.request("https://example.com").response { response in
// This session has its own cookie storage
}
Different Cookie Policies
// Configure different cookie acceptance policies
func configureCookiePolicy(_ policy: HTTPCookie.AcceptPolicy) -> Session {
let configuration = URLSessionConfiguration.default
configuration.httpCookieAcceptPolicy = policy
return Session(configuration: configuration)
}
// Examples of different policies:
let alwaysAcceptSession = configureCookiePolicy(.always)
let neverAcceptSession = configureCookiePolicy(.never)
let onlyFromMainDocumentSession = configureCookiePolicy(.onlyFromMainDocumentDomain)
Advanced Cookie Management
Cookie Persistence and Storage
class CookieManager {
private let cookieStorage: HTTPCookieStorage
init(storage: HTTPCookieStorage = .shared) {
self.cookieStorage = storage
}
// Save cookies to UserDefaults for persistence
func saveCookiesToUserDefaults(for domain: String) {
let cookies = cookieStorage.cookies?.filter { $0.domain.contains(domain) } ?? []
let cookieData = cookies.compactMap { try? NSKeyedArchiver.archivedData(withRootObject: $0, requiringSecureCoding: false) }
UserDefaults.standard.set(cookieData, forKey: "saved_cookies_\(domain)")
}
// Load cookies from UserDefaults
func loadCookiesFromUserDefaults(for domain: String) {
guard let cookieDataArray = UserDefaults.standard.array(forKey: "saved_cookies_\(domain)") as? [Data] else { return }
for cookieData in cookieDataArray {
if let cookie = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(cookieData) as? HTTPCookie {
cookieStorage.setCookie(cookie)
}
}
}
// Clear all cookies for a domain
func clearCookies(for domain: String) {
let cookies = cookieStorage.cookies?.filter { $0.domain.contains(domain) } ?? []
cookies.forEach { cookieStorage.deleteCookie($0) }
}
}
Session-Based Web Scraping
class WebScraper {
private let session: Session
private let cookieManager: CookieManager
init() {
let configuration = URLSessionConfiguration.default
configuration.httpCookieAcceptPolicy = .always
self.session = Session(configuration: configuration)
self.cookieManager = CookieManager(storage: configuration.httpCookieStorage!)
}
// Login and establish session
func login(username: String, password: String, completion: @escaping (Bool) -> Void) {
let parameters = ["username": username, "password": password]
session.request("https://example.com/login", method: .post, parameters: parameters)
.responseJSON { response in
let success = response.response?.statusCode == 200
if success {
// Save session cookies
self.cookieManager.saveCookiesToUserDefaults(for: "example.com")
}
completion(success)
}
}
// Scrape protected content
func scrapeProtectedContent(completion: @escaping (String?) -> Void) {
session.request("https://example.com/protected-data")
.responseString { response in
completion(response.value)
}
}
// Restore previous session
func restoreSession() {
cookieManager.loadCookiesFromUserDefaults(for: "example.com")
}
}
Cookie Debugging and Troubleshooting
// Comprehensive cookie debugging
func debugCookies() {
let allCookies = HTTPCookieStorage.shared.cookies ?? []
print("=== Cookie Debug Information ===")
print("Total cookies: \(allCookies.count)")
let groupedCookies = Dictionary(grouping: allCookies) { $0.domain }
for (domain, cookies) in groupedCookies {
print("\nDomain: \(domain)")
for cookie in cookies {
print(" 📋 \(cookie.name) = \(cookie.value)")
print(" Path: \(cookie.path)")
print(" Secure: \(cookie.isSecure ? "✅" : "❌") | HttpOnly: \(cookie.isHTTPOnly ? "✅" : "❌")")
if let expires = cookie.expiresDate {
let isExpired = expires < Date()
print(" Expires: \(expires) \(isExpired ? "⚠️ EXPIRED" : "✅")")
} else {
print(" Expires: Session cookie")
}
}
}
}
// Test cookie functionality
func testCookieFlow() {
print("Testing cookie flow...")
// 1. Clear existing cookies
HTTPCookieStorage.shared.cookies?.forEach {
HTTPCookieStorage.shared.deleteCookie($0)
}
// 2. Make initial request
AF.request("https://httpbin.org/cookies/set/test/value123")
.response { response in
print("Step 1 - Set cookie request completed")
self.debugCookies()
// 3. Make request that should include the cookie
AF.request("https://httpbin.org/cookies")
.responseJSON { response in
print("Step 2 - Cookie verification:")
if let json = response.value {
print(json)
}
}
}
}
Best Practices
- Use Custom Sessions: Create separate session instances for different scraping targets
- Handle Cookie Expiration: Check expiration dates and refresh tokens when needed
- Respect Cookie Scope: Pay attention to domain and path restrictions
- Secure Storage: Use Keychain for sensitive authentication cookies
- Error Handling: Always check for cookie creation failures
- Clean Up: Clear cookies when sessions end to prevent memory leaks
Common Pitfalls
- Domain Mismatches: Ensure cookie domains match your request URLs
- Path Restrictions: Cookies are only sent for matching paths
- Secure Flag: HTTPS-only cookies won't work with HTTP requests
- Expired Cookies: Check expiration dates, especially for long-running scrapers
Remember to always comply with website terms of service and respect rate limits when web scraping. Handle user data and cookies according to privacy regulations like GDPR and CCPA.