All Products
Search
Document Center

HTTPDNS:Best practices for using HTTPDNS with Alamofire on iOS

Last Updated:Oct 20, 2025

This document describes how to integrate HTTPDNS with Alamofire on an iOS client.

1. Overview

Alamofire is a popular networking framework for iOS development that provides an elegant API and powerful features. This document describes how to integrate HTTPDNS into a project that uses Alamofire.

For basic information about HTTPDNS and the technical challenges of using it on iOS, see Use HTTPDNS in native iOS scenarios.

2. Integration for standard HTTP and non-SNI HTTPS scenarios

This method applies to standard HTTP and non-SNI HTTPS scenarios.

2.1 Create a custom session

import Alamofire
import AlicloudHttpDNS

class AlamofireHttpsScenario {
    
    static let sharedSession: Session = {
        return Session(delegate: CustomerSessionDelegate())
    }()
}

2.2 Resolve domain names with HTTPDNS

class func resolveAvailableIp(host: String) -> String? {
    let httpDnsService = HttpDnsService.sharedInstance()
    let result = httpDnsService.resolveHostSyncNonBlocking(host, by: .auto)
    
    print("resolve host result: \(String(describing: result))")
    if result == nil {
        return nil
    }
    
    if result!.hasIpv4Address() {
        return result?.firstIpv4Address()
    } else if result!.hasIpv6Address() {
        return "[\(result!.firstIpv6Address())]"
    }
    return nil
}

2.3 Send network requests and handle certificate validation

class func httpDnsQueryWithURL(originalUrl: String, completionHandler: @escaping (_ message: String) -> Void) {
    var tipsMessage: String = ""guard let url = NSURL(string: originalUrl), let originalHost = url.host else {
        print("Error: invalid url: \(originalUrl)")
        return
    }
    
    let resolvedIpAddress = resolveAvailableIp(host: originalHost)
    
    var requestUrl = originalUrl
    if resolvedIpAddress != nil {
        // Replace the domain name with the resolved IP address.
        requestUrl = requestUrl.replacingOccurrences(of: originalHost, with: resolvedIpAddress!)
        
        let log = "Resolve host(\(originalHost)) by HTTPDNS successfully, result ip: \(resolvedIpAddress!)"print(log)
        tipsMessage = log
    } else {
        let log = "Resolve host(\(originalHost) by HTTPDNS failed, keep original url to request"print(log)
        tipsMessage = log
    }
    
    // Send the network request.
    sendRequestWithURL(requestUrl: requestUrl, host: originalHost) { message in
        tipsMessage = tipsMessage + "\n\n" + message
        completionHandler(tipsMessage)
    }
}

class func sendRequestWithURL(requestUrl: String, host: String, completionHandler: @escaping (_ message: String) -> Void) {
    // Important: Set the Host header to ensure the server correctly identifies the domain name.
    var header = HTTPHeaders()
    header.add(name: "host", value: host)
    
    sharedSession.request(requestUrl, method: .get, encoding: URLEncoding.default, headers: header)
        .validate()
        .response { response invar responseStr = ""switch response.result {
            case .success(let data):
                if let data = data, !data.isEmpty {
                    let dataStr = String(data: data, encoding: .utf8) ?? ""
                    responseStr = "HTTP Response: \(dataStr)"
                } else {
                    responseStr = "HTTP Response: [Empty Data]"
                }
            case .failure(let error):
                responseStr = "HTTP request failed with error: \(error.localizedDescription)"
            }
            
            completionHandler(responseStr)
        }
}

// Customize SessionDelegate to handle certificate validation.
class CustomerSessionDelegate: SessionDelegate {
    
    override func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        
        var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential: URLCredential?
        
        let request = task.currentRequest
        let host = request?.value(forHTTPHeaderField: "host") ?? ""
        // Important: Use the original domain name for certificate validation.
        if !host.isEmpty {
            if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
                if evaluate(serverTrust: challenge.protectionSpace.serverTrust, host: host) {
                    disposition = .useCredential
                    credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
                } else {
                    disposition = .performDefaultHandling
                }
            } else {
                disposition = .performDefaultHandling
            }
        }
        completionHandler(disposition, credential)
    }
    
    func evaluate(serverTrust: SecTrust?, host: String?) -> Bool {
        guard let serverTrust = serverTrust else {
            return false
        }
        
        // Create a certificate validation policy.
        var policies = [SecPolicy]()
        if let host = host {
            policies.append(SecPolicyCreateSSL(true, host as CFString))
        } else {
            policies.append(SecPolicyCreateBasicX509())
        }
        
        // Bind the validation policy to the server certificate.
        SecTrustSetPolicies(serverTrust, policies as CFTypeRef)
        
        // Evaluate the certificate trust.
        var result: SecTrustResultType = .invalid
        if SecTrustEvaluate(serverTrust, &result) == errSecSuccess {
            return result == .unspecified || result == .proceed
        } else {
            return false
        }
    }
}

3. Method 2: HTTPS + SNI scenario

This method applies to scenarios that require Server Name Indication (SNI) support, such as using a CDN or sharing an IP address among multiple domain names.

3.1 Configure a session that supports SNI

import Alamofire
import AlicloudHttpDNS

class AlamofireHttpsWithSNIScenario {
    
    static let sharedSession: Session = {
        let configuration = URLSessionConfiguration.af.default
        // Important: Register a custom NSURLProtocol to handle SNI.
        configuration.protocolClasses = [HttpDnsNSURLProtocolImpl.classForCoder()]
        return Session(configuration: configuration)
    }()
}

3.2 Resolve domain names with HTTPDNS

class func resolveAvailableIp(host: String) -> String? {
    let httpDnsService = HttpDnsService.sharedInstance()
    let result = httpDnsService.resolveHostSyncNonBlocking(host, by: .auto)
    
    print("resolve host result: \(String(describing: result))")
    if result == nil {
        return nil
    }
    
    if result!.hasIpv4Address() {
        return result?.firstIpv4Address()
    } else if result!.hasIpv6Address() {
        return "[\(result!.firstIpv6Address())]"
    }
    return nil
}

3.3 Send network requests in an SNI scenario

class func httpDnsQueryWithURL(originalUrl: String, completionHandler: @escaping (_ message: String) -> Void) {
    var tipsMessage: String = ""guard let url = NSURL(string: originalUrl), let originalHost = url.host else {
        print("Error: invalid url: \(originalUrl)")
        return
    }
    
    let resolvedIpAddress = resolveAvailableIp(host: originalHost)
    
    var requestUrl = originalUrl
    if resolvedIpAddress != nil {
        requestUrl = requestUrl.replacingOccurrences(of: originalHost, with: resolvedIpAddress!)
        
        let log = "Resolve host(\(originalHost)) by HTTPDNS successfully, result ip: \(resolvedIpAddress!)"print(log)
        tipsMessage = log
    } else {
        let log = "Resolve host(\(originalHost) by HTTPDNS failed, keep original url to request"print(log)
        tipsMessage = log
    }
    
    // Send the network request.
    sendRequestWithURL(requestUrl: requestUrl, host: originalHost) { message in
        tipsMessage = tipsMessage + "\n\n" + message
        completionHandler(tipsMessage)
    }
}

class func sendRequestWithURL(requestUrl: String, host: String, completionHandler: @escaping (_ message: String) -> Void) {
    // Set the Host header.
    var header = HTTPHeaders()
    header.add(name: "host", value: host)
    
    // Note: SNI and certificate validation are handled at the protocol layer because a custom NSURLProtocol is used. You can send the request directly.
    sharedSession.request(requestUrl, method: .get, encoding: URLEncoding.default, headers: header)
        .validate()
        .response { response invar responseStr = ""switch response.result {
            case .success(let data):
                if let data = data, !data.isEmpty {
                    let dataStr = String(data: data, encoding: .utf8) ?? ""
                    responseStr = "HTTP Response: \(dataStr)"
                } else {
                    responseStr = "HTTP Response: [Empty Data]"
                }
            case .failure(let error):
                responseStr = "HTTP request failed with error: \(error.localizedDescription)"
            }
            
            completionHandler(responseStr)
        }
}

Note: In an SNI scenario, certificate validation and domain name handling are automatically processed at the NSURLProtocol layer. You do not need to configure an extra certificate validation callback.

For a sample implementation, see HttpDnsNSURLProtocolImpl.m in the Alibaba Cloud httpdns_ios_demo. You can modify or reuse this sample as needed.

4. Examples

4.1 Basic HTTPS request

// Standard HTTPS request (non-SNI scenario)
AlamofireHttpsScenario.httpDnsQueryWithURL(originalUrl: "https://example.com/api/data") { 
  message inprint("Request result: \(message)")
}

4.2 SNI scenario request

// SNI scenario request (for example, a CDN)
AlamofireHttpsWithSNIScenario.httpDnsQueryWithURL(originalUrl: "https://cdn.example.com/api/data") { 
  message inprint("Request result: \(message)")
}

5. Summary

The core steps to integrate HTTPDNS with Alamofire are:

  1. Initialize the session - Configure basic network parameters and the proxy.

  2. Resolve the domain name with HTTPDNS - Obtain the IP address to replace the domain name.

  3. Send the network request - Set the Host header and handle HTTPS certificate validation.

Key points

  • Host header: Ensure the server can correctly identify the requested domain name.

  • Certificate validation: Use the original domain name, not the IP address, for certificate validation.

  • SNI handling: Use NSURLProtocol for automatic handling in complex scenarios.

  • Fallback policy: Fall back to the system DNS if HTTPDNS resolution fails.

For the complete sample code, see the HTTPDNS iOS Demo.