All Products
Search
Document Center

Alibaba Cloud DNS:iOS SDK development guide

Last Updated:Mar 17, 2026

This document describes how to integrate and use the HTTPDNS iOS SDK.

Overview

The iOS SDK encapsulates the DoH JSON API for HTTPDNS. It provides domain name resolution functions for iOS apps and an efficient domain name caching feature based on Time-to-Live (TTL) and Least Recently Used (LRU) policies. Using this SDK, you can easily integrate HTTPDNS into your iOS apps to resolve domain name resolution exceptions and implement precise, low-cost scheduling for domain name resolution.

Starting from iOS 14, the system natively supports two standard encrypted DNS protocols: DNS over TLS (DoT) and DNS over HTTPS (DoH). For more information about how to set HTTPDNS as the default encrypted DNS resolver, see iOS 14 native encrypted DNS solution.

The SDK has the following advantages:

  • Simple and easy to use

    Integrate the SDK to access the HTTPDNS service. The integration method is simple and provides a convenient resolution service.

  • Zero latency

    The SDK implements an internal LRU caching mechanism to cache IP addresses from domain name resolutions locally. It also actively updates expired cache entries to ensure that cached data remains timely and effective. This helps you achieve zero-latency domain name resolution.

For more information about how to use this SDK in an Objective-C project, see the alidns_ios_demo sample project source code.

For more information about how to use this SDK in a Swift project, see the DNSResolverSwiftDemo sample project source code.

SDK integration

Import the SDK

Integrate using CocoaPods

  1. In the Podfile, specify the repository location. Do not omit the Master repository.

    source 'https://github.com/CocoaPods/Specs.git'
    source 'https://github.com/aliyun/aliyun-specs.git'
  2. Add a dependency for the project target.

    pod 'AlicloudPDNS'

Integrate manually

  1. For more information, see SDK download to obtain the iOS SDK.

  2. After obtaining the SDK's pdns-sdk-ios.framework, manually integrate it into your project.

  3. Import system libraries:

    • Foundation.framework

    • SystemConfiguration.framework

    • CoreFoundation.framework

    • CoreTelephony.framework

  4. In the project's Build Settings, add the -ObjC flag in the Other linker flags section.

SDK initialization

First, register your application in the console to obtain its unique identifier and authentication parameters. Then, initialize the SDK after integration.

Important

To ensure the SDK works correctly and to avoid resolution failures, initialize the SDK as early as possible.

Initialize the SDK in application:didFinishLaunchingWithOptions:.

DNSResolver *resolver = [DNSResolver share];
//setAccountId:@"******": Replace the asterisk content with the Account ID from the Access Configuration page in the console.
//andAccessKeyId:@"********": Replace the asterisk content with the AccessKey ID of the key that you created on the Access Configuration page in the console.
//andAccesskeySecret:@"********": Replace the asterisk content with the AccessKey secret of the key that you created on the Access Configuration page in the console.
[resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
// Specify domains for automatic expired cache updates. The array is limited to a maximum of 10 domain names.
[resolver setKeepAliveDomains:@[@"user_specified_domain_1",@"user_specified_domain_2"]];
// Preload domains that may need to be resolved later.
[resolver preloadDomains:@[@"domain1", @"domain2", @"domain3"] complete:^{
// All domains are preloaded.
}];

API Introduction

Common settings

1. Account ID and authentication

These are required parameters. After you register your application in the console, the console generates a unique Account ID for the application. The authentication feature ensures user identity security and prevents unauthorized use by third parties. For more information, see Create an AccessKey pair to create an AccessKey in the console. Set it in the app using the following code:

//setAccountId:@"******": Replace the asterisk content with the Account ID from the Access Configuration page in the console.
//andAccessKeyId:@"********": Replace the asterisk content with the AccessKey ID of the key that you created on the Access Configuration page in the console.
//andAccesskeySecret:@"********": Replace the asterisk content with the AccessKey secret of the key that you created on the Access Configuration page in the console.
[[DNSResolver share] setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
Warning
  • To avoid leaking your Account ID, AccessKey ID, AccessKey secret, or other data generated during app operation in logs, disable SDK debugging logs in the release version.

  • For demonstration purposes, the sample code directly passes the Account ID, AccessKey ID, and AccessKey secret as properties. These parameters are closely related to metering and billing. To prevent information leakage from malicious decompilation, do not pass plaintext credentials directly in a production environment. For example, you can encode or encrypt the plaintext credentials beforehand and then decode or decrypt them when passing the values. We also recommend that you obfuscate and reinforce your app's code. Otherwise, your Account ID, AccessKey ID, and AccessKey secret may be obtained by third parties through decompilation.

2. Resolution protocol settings

The SDK lets you set the protocol for DNS resolution requests. You can choose to resolve through HTTP or HTTPS by setting the scheme property.

By default, the SDK uses and recommends the HTTPS protocol for resolution because HTTPS provides better security. HTTPDNS is billed based on the number of HTTP resolution requests. HTTPS resolution requests are billed at five times the rate of HTTP resolution requests. Select a scheme type as needed. Set the property as follows:

[DNSResolver share].scheme = DNSResolverSchemeHttps;

3. Enable or disable caching

You can enable the caching feature in the SDK. If caching is enabled, after a domain name is resolved for the first time, subsequent resolutions will prioritize fetching data from the cache. This can greatly improve resolution speed.

The SDK enables caching by default. To disable caching, use the following code:

[DNSResolver share].cacheEnable=NO;

4. Set keep-alive cache for domains

When caching is enabled, you can enable the keep-alive cache feature for specific domains. If this feature is enabled, the SDK automatically updates the expired cache for these domains to ensure that the cached data is up to date. However, this may increase the number of domain name resolutions and client traffic consumption. If this feature is not enabled, the SDK does not automatically update the expired cache. The cache is updated only when you call a resolution method. To set the keep-alive cache for specific domains, use the following code:

// The array is limited to a maximum of 10 domain names.
[[DNSResolver share] setKeepAliveDomains:@[@"www.taobao.com",@"www.aliyun.com"]];
Note
  • Advantages:

    • Updates records promptly (before the TTL expires).

    • Reduces the initial resolution delay to 0 ms when used with pre-resolution.

  • Disadvantages: Re-requesting at 75% of the TTL incurs additional costs.

5. Pre-resolution

Because the SDK's caching feature can be enabled, the resolution speed for a domain can be reduced to zero latency on subsequent requests after the initial resolution generates a cache entry. For best performance, pre-resolve the domains that your app may need to resolve after the app starts.

Code example:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   // Override point for customization after application launch.
   
   DNSResolver *resolver = [DNSResolver share];
   //setAccountId:@"******": Replace the asterisk content with the Account ID from the Access Configuration page in the console.
   //andAccessKeyId:@"********": Replace the asterisk content with the AccessKey ID of the key that you created on the Access Configuration page in the console.
   //andAccesskeySecret:@"********": Replace the asterisk content with the AccessKey secret of the key that you created on the Access Configuration page in the console.
   [resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
   resolver.cacheEnable = YES;
   // Preload domains that may need to be resolved later.
   [resolver preloadDomains:@[@"domain1", @"domain2", @"domain3"] complete:^{
       // All domains are preloaded.
       
   }];
   
   return YES;
}

Advanced settings

1. Use a server-side IPv6 address

The HTTPDNS service supports IPv4 and IPv6 dual-stack access. By default, the SDK uses an IPv4 address to access a DNS server for resolution.

To access DNS servers for resolution through an IPv6 address, the current network must support IPv6. Use the following code to enable this feature:

[DNSResolver share].ipv6Enable = YES;

2. Short mode

The DoH JSON API of HTTPDNS returns data in either a full JSON format or a brief IP address array format. By default, the SDK uses the full JSON format.

To use the brief IP address array format, use the following code:

[DNSResolver share].shortEnable = YES;

3. Set the cache size

If caching is enabled in the SDK, you can customize the number of cached entries. The supported range is from 100 to 500.

The default cache size is 100 domain names. To customize the cache size, set the cacheCountLimit property:

[DNSResolver share].cacheCountLimit = 200;

4. Configure IP speed test enablement

You can enable IP address probing in the SDK. If IP address probing is enabled, the resolution result prioritizes the fastest IP address. The array of IP addresses is sorted from fastest to slowest based on the probing results.

The SDK does not enable IP speed testing by default. To enable IP speed testing, use the following code:

[DNSResolver share].speedTestEnable=YES;

5. Set the IP address probing method

The SDK can set the method for IP speed testing. If IP speed testing is enabled and this parameter is set to 0, ICMP detection will be used. If this parameter is set to 80, 443, or other supported port numbers, socket-specific port detection will be used.

The default value for this parameter is 443.

[DNSResolver share].speedPort = 80;

6. Configure ISP-specific domain name caching

You can use the SDK to enable domain name caching based on ISP networks. If enabled, the cached domain name resolution results are separately stored in different network environments without affecting each other. If not enabled, the same cached domain name resolution results are used across different networks.

By default, the SDK enables domain name cache differentiation based on the ISP network.

[DNSResolver share].ispEnable = YES;

7. Set maximum TTL for negative cache

The SDK can set the maximum TTL for negative cache (a cache for a failed domain name lookup that returns no IP address). If this value is set, the maximum TTL of the negative cache will not exceed the specified duration.

The default value of this parameter in the SDK is 30 seconds. To set the maximum TTL for the negative cache, use the following code:

[DNSResolver share].maxNegativeCache = 30;

8. Set maximum cache TTL

You can use the SDK to set a maximum TTL for the cache to ensure that the TTL of cached data does not exceed this limit.

By default, the SDK sets this parameter to 3600 s. To set the maximum cache TTL, use the following code:

[DNSResolver share].maxCacheTTL= 3600;

9. Set whether to enable immutable cache

[DNSResolver share].immutableCacheEnable = NO;// Immutable cache is disabled by default and never expires
Important

The SDK uses three cache update mechanisms:

  • Immutable cache: Enable this feature to treat the cache as always valid during app runtime. The SDK skips cache expiration checks and updates, minimizing DNS resolution requests.

    Set it as follows: [DNSResolver share].immutableCacheEnable = YES

  • Active cache update: This mechanism ensures DNS resolution hits the latest cached record. When a domain’s authoritative DNS record changes, active updates let resolution requests use the cache—reducing DNS latency—while typically keeping the cached records up to date. The domain list can contain up to 10 domains.

    Set it as follows: [[DNSResolver share] setKeepAliveDomains:@[@"user-specified-domain-1",@"user-specified-domain-2"]]

  • Passive cache update: The cache updates passively when you call either of these two methods to get resolution results:

    • - (void)getIpv4DataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))complete: Get the IPv4 address array for a domain. If the cache is not empty and within its TTL, return the cached result directly. Otherwise, fetch the latest result over the network, return it, and update the cache. Use this method when you need high accuracy in resolution results.

    • - (NSArray<NSString *> *)getIpv4ByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable: Get IPv4 resolution results from the cache. This method returns expired cache entries based on the value of the enable parameter.

      Metric description: If enable is YES, return stale records even if the cache has expired (return nil if the cache is empty) and update the cache using an asynchronous request. If NO, return nil when the cache is expired or empty, and update the cache using an asynchronous request.

10. Timeout

The timeout property specifies the timeout for domain name resolution. The default timeout is 3 seconds. Users can customize the timeout, and it is recommended to set it between 2 and 5 seconds.

Service APIs

Code example:

/// Pre-resolves domain information. This can be called when the program starts. The resolution results are stored in the cache to speed up subsequent domain name resolutions.
/// Automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack) to resolve IP addresses suitable for the current network.
/// @param domainArray  An array of domain names.
/// @param complete     The callback after resolution is complete.
- (void)preloadDomains:(NSArray<NSString *> *)domainArray complete:(void(^)(void))complete;

/// Gets the array of IP addresses after domain name resolution. It automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack) to get IP addresses suitable for the current network.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP addresses through a network request. If caching is not enabled, it directly gets the corresponding IP addresses through a network request.
/// @param domain           The domain name.
/// @param complete       The callback (all IP addresses).
- (void)getIpsDataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))complete;

/// Automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack) to directly get an array of IP addresses suitable for the current network from the cache, without waiting.  
/// If there is no cache, it returns nil. If there is a cache and enable is set to YES, it returns the cached data and asynchronously resolves the domain name to update the cache if the data has expired. If there is a cache and enable is set to NO, it returns nil when the cache has expired and asynchronously resolves the domain name to update the cache.
/// @param domain   The domain name.
/// @param enable   Specifies whether to allow returning expired IP addresses.
- (NSArray<NSString *> *)getIpsByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable;

/// Gets the array of IPv4 information after domain name resolution.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP addresses through a network request. If caching is not enabled, it directly gets the corresponding IP addresses through a network request.
/// @param domain           The domain name.
/// @param complete       The callback (all domain information).
- (void)getIpv4InfoWithDomain:(NSString *)domain complete:(void(^)(NSArray<DNSDomainInfo *> *domainInfoArray))complete;

/// Gets the array of IPv6 information after domain name resolution.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP addresses through a network request. If caching is not enabled, it directly gets the corresponding IP addresses through a network request.
/// @param domain           The domain name.
/// @param complete       The callback (all domain information).
- (void)getIpv6InfoWithDomain:(NSString *)domain complete:(void(^)(NSArray<DNSDomainInfo *> *domainInfoArray))complete;

/// Gets the IPv4 information after domain name resolution.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP address through a network request. If caching is not enabled, it directly gets the corresponding IP address through a network request.
/// @param domain           The domain name.
/// @param complete       The callback (a random entry from all domain information).
- (void)getRandomIpv4InfoWithDomain:(NSString *)domain complete:(void(^)(DNSDomainInfo *domainInfo))complete;

/// Gets the IPv6 information after domain name resolution.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP address through a network request. If caching is not enabled, it directly gets the corresponding IP address through a network request.
/// @param domain           The domain name.
/// @param complete       The callback (a random entry from all domain information).
- (void)getRandomIpv6InfoWithDomain:(NSString *)domain complete:(void(^)(DNSDomainInfo *domainInfo))complete;

/// Gets the array of IPv4 addresses after domain name resolution.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP addresses through a network request. If caching is not enabled, it directly gets the corresponding IP addresses through a network request.
/// @param domain           The domain name.
/// @param complete       The callback (all IP addresses).
- (void)getIpv4DataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))complete;

/// Gets the array of IPv6 addresses after domain name resolution.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP addresses through a network request. If caching is not enabled, it directly gets the corresponding IP addresses through a network request.
/// @param domain           The domain name.
/// @param complete       The callback (all IP addresses).
- (void)getIpv6DataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))complete;

/// Gets an IPv4 address after domain name resolution.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP address through a network request. If caching is not enabled, it directly gets the corresponding IP address through a network request.
/// @param domain            The domain name.
/// @param complete        The callback (a random address from all IP addresses).
- (void)getRandomIpv4DataWithDomain:(NSString *)domain complete:(void(^)(NSString *data))complete;

/// Gets an IPv6 address after domain name resolution.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP address through a network request. If caching is not enabled, it directly gets the corresponding IP address through a network request.
/// @param domain            The domain name.
/// @param complete        The callback (a random address from all IP addresses).
- (void)getRandomIpv6DataWithDomain:(NSString *)domain complete:(void(^)(NSString *data))complete;

/// Pre-resolves domain IPv4 information. This can be called when the program starts. The resolution results are stored in the cache to speed up subsequent domain name resolutions.
/// @param domainArray  An array of domain names.
/// @param complete         The callback after resolution is complete.
- (void)preloadIpv4Domains:(NSArray<NSString *> *)domainArray complete:(void(^)(void))complete;

/// Pre-resolves domain IPv6 information. This can be called when the program starts. The resolution results are stored in the cache to speed up subsequent domain name resolutions.
/// @param domainArray  An array of domain names.
/// @param complete         The callback after resolution is complete.
- (void)preloadIpv6Domains:(NSArray<NSString *> *)domainArray complete:(void(^)(void))complete;

/// Directly gets IPv4 resolution results from the cache, without waiting.  
/// If there is no cache, it returns nil. If there is a cache and enable is set to YES, it returns the cached data and asynchronously resolves the domain name to update the cache if the data has expired. If there is a cache and enable is set to NO, it returns nil when the cache has expired and asynchronously resolves the domain name to update the cache.
/// @param domain   The domain name.
/// @param enable   Specifies whether to allow returning expired IP addresses.
- (NSArray<NSString *> *)getIpv4ByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable;

/// Directly gets IPv6 resolution results from the cache, without waiting.  
/// If there is no cache, it returns nil. If there is a cache and enable is set to YES, it returns the cached data and asynchronously resolves the domain name to update the cache if the data has expired. If there is a cache and enable is set to NO, it returns nil when the cache has expired and asynchronously resolves the domain name to update the cache.
/// @param domain   The domain name.
/// @param enable   Specifies whether to allow returning expired IP addresses.
- (NSArray<NSString *> *)getIpv6ByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable;

///Collects statistics information.
-(NSArray *)getRequestReportInfo;

API usage examples

1. Configure basic information

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   // Override point for customization after application launch.
   // Unique initialization method
   DNSResolver *resolver = [DNSResolver share];
   //setAccountId:@"******": Replace the asterisk content with the Account ID from the Access Configuration page in the console.
   //andAccessKeyId:@"********": Replace the asterisk content with the AccessKey ID of the key that you created on the Access Configuration page in the console.
   //andAccesskeySecret:@"********": Replace the asterisk content with the AccessKey secret of the key that you created on the Access Configuration page in the console.
   [resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
   // Specify domains for automatic expired cache updates. The array is limited to a maximum of 10 domain names.
	 [resolver setKeepAliveDomains:@[@"user_specified_domain_1",@"user_specified_domain_2"]];
   // Preload domains that may need to be resolved later to get resolution results in advance and store them in the cache.
   [resolver preloadDomains:@[@"domain1", @"domain2", @"domain3"] complete:^{
       // All domains are preloaded.
       
   }];
   return YES;
}

2. Domain name resolution API

The SDK provides multiple domain name resolution methods, which you can find in the DNSResolver.h header file. The following example describes a resolution method that automatically distinguishes between network environments (IPv4-only, IPv6-only, and IPv4 and IPv6 dual-stack).

API declaration:

/// Gets the array of IP addresses after domain name resolution. It automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack) to get IP addresses suitable for the current network.
/// If caching is enabled, it prioritizes returning data from the cache. If there is no cache or the cache has expired, it gets the corresponding IP addresses through a network request. If caching is not enabled, it directly gets the corresponding IP addresses through a network request.
/// @param domain           The domain name.
/// @param complete       The callback (all IP addresses).
- (void)getIpsDataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))complete;

API call example:

[[DNSResolver share] getIpsDataWithDomain:@"www.taobao.com" complete:^(NSArray<NSString *> *dataArray) {
    // dataArray is the array of IP addresses for the domain name www.taobao.com.
   if (dataArray.count > 0) {
       //TODO: Use an IP address for the URL connection.
   }    
}];

3. Retrieve resolution results directly from the cache

API declaration:

/// Automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack) to directly get an array of IP addresses suitable for the current network from the cache, without waiting.  
/// If there is no cache, it returns nil. If there is a cache and enable is set to YES, it returns the cached data and asynchronously resolves the domain name to update the cache if the data has expired. If there is a cache and enable is set to NO, it returns nil when the cache has expired and asynchronously resolves the domain name to update the cache.
/// @param domain   The domain name.
/// @param enable   Specifies whether to allow returning expired IP addresses.
- (NSArray<NSString *> *)getIpsByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable;

Call example:

NSArray *result = [[DNSResolver share] getIpsByCacheWithDomain:@"domain_name" andExpiredIPEnabled:YES];
// Get the cached result.
if (result.count > 0) {
   //TODO: Use an IP address for the URL connection.
   
}

Note: Retrieving results directly from the cache is faster. However, the result will be nil if there is no cache, or if the cached resolution result has expired and enable is set to NO.

4. Delete the cache

API declaration:

/// hostArray is the array of host domain names to clear from the cache. To clear all data, pass nil or an empty array.
-(void)clearHostCache:(NSArray <NSString *>*)hostArray;

Sample code:

[[DNSResolver share] clearHostCache:@[@"domain 1", @"domain 2"]];

5. Statistics collection

Interface declaration:

///Statistics information collection
-(NSArray *)getRequestReportInfo;

Sample code:

NSArray *array = [[DNSResolver share] getRequestReportInfo];

Data format:

 (
      {
         avgRtt = "1";                                // Average domain resolution time in ms
         cacheDnsNum = 0;                             // Number of cache hits                       
         domain = "www.taobao.com";                   // Domain name being resolved
         gobackLocaldnsNum = 0;                       // Number of times downgraded to LocalDNS
         localErro = 0;                               // Number of LocalDNS resolution failures
         maxRtt = "60";                               // Maximum domain resolution time in ms
         noPermissionErro = 0;                        // Number of user authentication failures
         noResponseErro = 0;                          // Number of request timeouts with no response          
         requestPDnsNum = 1;                          // Number of recursive queries
         sp = "China Mobile";                         // ISP name
         successNum = 1;                              // Number of successful resolutions
         timeoutErro = 0;                             // Number of network timeout errors
         type = 28;                                   // IP type, 1 represents IPv4, 28 represents IPv6
         urlParameterErro = 0;                        // Number of request parameter format errors
         urlPathErro = 0;                             // Number of URL errors
      }
         ......
 );

Best practices for domain name resolution

Combine pre-resolution with allowing expired responses to achieve optimal performance.

In scenarios that require high network performance, combining the pre-resolution and allow expired responses strategies can significantly improve DNS resolution speed and even achieve zero-latency resolution.

Thanks to the SDK's built-in caching mechanism, after pre-resolution is complete, subsequent resolution requests for the domain can directly hit the cache. This avoids network round-trips and greatly optimizes the user experience.

1. Pre-resolution

Enable caching and pre-resolve key domain names. We recommend doing this when the app starts.

In the AppDelegate's application:didFinishLaunchingWithOptions: method, pre-resolve the domain names used in your business and cache the results in local memory.

1. For scenarios that support only IPv4

//********For scenarios that support only IPv4*******
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   DNSResolver *resolver = [DNSResolver share];
   //setAccountId:@"******": Replace the asterisk content with the Account ID from the Access Configuration page in the console.
   //andAccessKeyId:@"********": Replace the asterisk content with the AccessKey ID of the key that you created on the Access Configuration page in the console.
   //andAccesskeySecret:@"********": Replace the asterisk content with the AccessKey secret of the key that you created on the Access Configuration page in the console.
   [resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
   // Specifies whether to use the cache. The default value is YES.
   resolver.cacheEnable = YES;
   // Preload domains that may need to be resolved later. This pre-resolves domain IPv4 information.
   [resolver preloadIpv4Domains:@[@"domain1", @"domain2", @"domain3"] complete:^{
       // All domains are preloaded.
   }];
   return YES;
}

2. For scenarios that need to support IPv6

//********For scenarios that need to support IPv6*******
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   DNSResolver *resolver = [DNSResolver share];
   //setAccountId:@"******": Replace the asterisk content with the Account ID from the Access Configuration page in the console.
   //andAccessKeyId:@"********": Replace the asterisk content with the AccessKey ID of the key that you created on the Access Configuration page in the console.
   //andAccesskeySecret:@"********": Replace the asterisk content with the AccessKey secret of the key that you created on the Access Configuration page in the console.
   [resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
   // Specifies whether to use the cache. The default value is YES.
   resolver.cacheEnable = YES;
   // Specifies whether to use an IPv6 network to resolve domain names. The default value is NO.
   resolver.ipv6Enable = YES;
   // Specifies whether to enable IP address probing. The default value is NO.
   resolver.speedTestEnable = YES;
   // Preload domains that may need to be resolved later. This automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack) to resolve IP addresses suitable for the current network.
   [resolver preloadDomains:@[@"domain1", @"domain2", @"domain3"] complete:^{
       // All domains are preloaded.
   }];
   return YES;
}

2. Allow expired responses

When resolving, prioritize using the cache and allow returning expired IP addresses. Before initiating a network request, first get an IP address from the cache and allow the use of expired but still valid cache records (andExpiredIPEnabled:YES). This way, even if the TTL has passed, a result can be returned immediately as long as the cache has not been cleared, achieving zero-wait resolution.

1. For scenarios that support only IPv4

    //********For scenarios that support only IPv4*******
      __weak typeof(self) ws = self;
    // Prioritize fetching the IP from the cache (ExpiredIPEnabled is YES, allowing the return of expired but available records).
    NSArray<NSString *> *cachedIPs = [[DNSResolver share] getIpv4ByCacheWithDomain:domain andExpiredIPEnabled:YES];
    if (cachedIPs && cachedIPs.count > 0) {
        NSString *ip = cachedIPs.firstObject;
        NSLog(@"Cache hit quickly. Domain %@ resolved to IP: %@", domain, ip);
        [self requestWithIP:ip domain:domain]; // Use the IP for a direct connection.
    } else {
        // If the cache is missed, initiate an asynchronous resolution.
        [[DNSResolver share] getIpv4DataWithDomain:domain complete:^(NSArray<NSString *> *resolvedIPs) {
            if (resolvedIPs && resolvedIPs.count > 0) {
                NSString *ip = resolvedIPs.firstObject;
                NSLog(@"Asynchronous resolution complete. Domain %@ resolved to IP: %@", domain, ip);
                [ws requestWithIP:ip domain:domain]; // Use the IP for a direct connection.
            } else {
                NSLog(@"Domain name resolution failed. Falling back to the original domain name.");
                [ws requestWithIP:domain domain:domain]; // Fallback: Use the original domain name.
            }
        }];
    }

2. For scenarios that need to support IPv6

    //********For scenarios that need to support IPv6*******
      __weak typeof(self) ws = self;
    // Prioritize fetching the IP from the cache (ExpiredIPEnabled is YES, allowing the return of expired but available records).
    NSArray<NSString *> *cachedIPs = [[DNSResolver share] getIpsByCacheWithDomain:domain andExpiredIPEnabled:YES];
    if (cachedIPs && cachedIPs.count > 0) {
        NSString *ip = cachedIPs.firstObject;
        NSLog(@"Cache hit quickly. Domain %@ resolved to IP: %@", domain, ip);
        [self requestWithIP:ip domain:domain]; // Use the IP for a direct connection.
    } else {
        // If the cache is missed, initiate an asynchronous resolution.
        [[DNSResolver share] getIpsDataWithDomain:domain complete:^(NSArray<NSString *> *resolvedIPs) {
            if (resolvedIPs && resolvedIPs.count > 0) {
                NSString *ip = resolvedIPs.firstObject;
                NSLog(@"Asynchronous resolution complete. Domain %@ resolved to IP: %@", domain, ip);
                [ws requestWithIP:ip domain:domain]; // Use the IP for a direct connection.
            } else {
                NSLog(@"Domain name resolution failed. Falling back to the original domain name.");
                [ws requestWithIP:domain domain:domain]; // Fallback: Use the original domain name.
            }
        }];
    }

Notes

  1. pdns-sdk-ios.framework supports a minimum iOS version of 9.0.

  2. When making requests using the HTTP protocol, you need to set App Transport Security Settings->Allow Arbitrary Loads to YES in Info.plist.

  3. After you obtain the IP address of a domain name through HTTPDNS, the client can use this IP address to send service requests. The Host field in the HTTP request header must be set to the original domain name.

    For example:

    // ip is the IP address resolved from the original domain name.
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", ip]];
    NSMutableURLRequest *mutableReq = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval: 10];
    // Set the host.
    [mutableReq setValue:@"original_domain_name" forHTTPHeaderField:@"host"];
  4. To ensure your services run correctly, if the SDK fails to get an IP address for a domain name, use the original domain name for the request as a fallback. The following code provides an example:

    NSArray *array = [[DNSResolver share] getIpsByCacheWithDomain:@"original_domain_name" andExpiredIPEnabled:YES];
    if (array && array.count >0 && array.firstObject.length > 0) {
        // Replace the host in the URL with the IP address for the API request.
        
    }else{
        // Add fallback handling (use the original URL for the API request).
    }
  5. If an intermediate HTTP proxy exists, the request line in client-initiated requests uses an absolute path URL. If you enable HTTPDNS and use an IP-based URL for access, the intermediate proxy identifies your IP information and passes it as the real host information to the target server. In this case, the target server cannot process HTTP requests that do not contain the real host information. We recommend that you check whether a network proxy is enabled on the current device. If a proxy is enabled, do not use HTTPDNS for domain name resolution.

On-premises DNS

Starting from v2.3.0, the HTTPDNS iOS SDK supports on-premises DNS for private deployments.

The on-premises DNS mode is suitable for scenarios with high requirements for data compliance and custom resolution policies, such as finance, government, and large internet enterprises. The SDK supports four typical deployment modes to flexibly adapt to different business architectures: public cloud DNS only, on-premises DNS only, and primary/backup hybrid modes where public cloud DNS and on-premises DNS serve as mutual backups.

Core features

  • Private deployment support: Supports configuring on-premises DNS server endpoints using IPv4 or IPv6 addresses, or host domain names.

  • Bidirectional authentication: Uses customer-specific accessKeyId and accessKeySecret to sign requests, ensuring communication security.

  • Circuit breaking and health checks: Automatically breaks the circuit if an on-premises DNS node fails three or more consecutive times. It then probes the node's availability every minute using a specified healthCheckDomain. The node is automatically re-enabled after it recovers.

  • Certificate validation control: Supports enabling or disabling TLS certificate validation. We strongly recommend enabling it in production environments.

  • Smart failover: Automatically switches to the backup DNS when the primary DNS (public cloud or on-premises) fails to resolve requests and reaches a specified threshold. This ensures high availability for resolution.

  • Seamless API compatibility: The way you call the resolution APIs remains the same whether you use public cloud DNS or on-premises DNS. You do not need to modify your business logic.

Configuration

1. Use only public cloud DNS

This mode is for standard SaaS users who have not deployed an on-premises DNS.

DNSResolver *resolver = [DNSResolver share];
[resolver setAccountId:@"******" 
        andAccessKeyId:@"********" 
    andAccesskeySecret:@"********"];

2. Use only on-premises DNS (private deployment)

This mode is for customers who rely entirely on their on-premises DNS.

DNSResolver *resolver = [DNSResolver share];
[resolver setFusionDNSWithIPv4:@[@"1.1.X.X", @"2.2.X.X"]
                            IPv6:nil
                           Host:nil
                           Port:@"443"
              HealthCheckDomain:@"check.example.com"
                    accessKeyId:@"your_fusion_ak"
                accesskeySecret:@"your_fusion_sk"];
// Optional: Disable certificate validation (for test environments only).
// [resolver setEnableCertificateValidation:NO];

3. Public cloud DNS as primary, on-premises DNS as backup

If the primary Alibaba Cloud public HTTPDNS fails, it automatically fails over to the on-premises DNS.

DNSResolver *resolver = [DNSResolver share];

// Primary: Public cloud DNS
[resolver setAccountId:@"******" 
        andAccessKeyId:@"********" 
    andAccesskeySecret:@"********"];
    
// Backup: On-premises DNS
[resolver setFusionDNSWithIPv4:@[@"1.1.X.X", @"2.2.X.X"]
                            IPv6:nil
                           Host:nil
                           Port:@"443"
              HealthCheckDomain:@"check.example.com"
                    accessKeyId:@"your_fusion_ak"
                accesskeySecret:@"your_fusion_sk"];
// Optional: Disable certificate validation (for test environments only).
// [resolver setEnableCertificateValidation:NO];

4. On-premises DNS as primary, public cloud DNS as backup

If the primary on-premises DNS fails, it automatically fails over to the Alibaba Cloud public HTTPDNS.

DNSResolver *resolver = [DNSResolver share];

// Primary: On-premises DNS
[resolver setFusionDNSWithIPv4:@[@"1.1.X.X", @"2.2.X.X"]
                            IPv6:nil
                           Host:nil
                           Port:@"443"
              HealthCheckDomain:@"check.example.com"
                    accessKeyId:@"your_fusion_ak"
                accesskeySecret:@"your_fusion_sk"];
// Optional: Disable certificate validation (for test environments only).
// [resolver setEnableCertificateValidation:NO];

// Backup: Public cloud DNS
[resolver setAccountId:@"******" 
        andAccessKeyId:@"********" 
    andAccesskeySecret:@"********"];

New service APIs

To support private deployments and high-availability disaster recovery for on-premises DNS, the SDK adds the following three core APIs. These APIs are used to configure the on-premises DNS service, control security policies, and implement automatic primary/backup failover.

1. Configure on-premises DNS server endpoints and authentication information

/** This is related to on-premises DNS for private deployments. Do not call this method if you only use public DNS.
 *
 * Sets the on-premises DNS server address and authentication information.
 * Customers pass the private DNS server's address and authentication credentials through this API.
 * The SDK will use this information to initiate requests.
 * @param ipv4 An array of IPv4 addresses (can be nil).
 * @param ipv6 An array of IPv6 addresses (can be nil).
 * @param host An array of host domain names (can be nil).
 * @param port The service port (such as @"443". If nil, the default is used).
 * @param healthCheckDomain The domain name for health checks after a circuit break. If a resolution service fails more than 3 consecutive times, the circuit breaks, and the service's IP address enters a healthCheck state. Subsequent requests will not use this service. A timer probes this healthCheckDomain every minute to check if the service is available. If the probe succeeds, the service state is restored to alive and can be used for future requests.
 * @param accessKeyId The customer's private accessKeyId (for authentication).
 * @param accesskeySecret The customer's private accesskeySecret (for authentication).
 */
- (void)setFusionDNSWithIPv4:(NSArray<NSString *> * _Nullable)ipv4
                        IPv6:(NSArray<NSString *> * _Nullable)ipv6
                        Host:(NSArray<NSString *> * _Nullable)host
                        Port:(NSString * _Nullable)port
           HealthCheckDomain:(NSString * _Nonnull)healthCheckDomain
                 accessKeyId:(NSString * _Nonnull)accessKeyId
             accesskeySecret:(NSString * _Nonnull)accesskeySecret;

2. Control TLS certificate validation for on-premises DNS

/** This is related to on-premises DNS for private deployments. Do not call this method if you only use public DNS.
 *
 * Specifies whether to enable certificate validation for on-premises DNS (default is YES). If the server is not configured with a domain name certificate and an IP certificate, you can set this to NO for testing. For production environments, we strongly recommend setting this to YES to avoid security risks.
 * @param enable YES to enable (default), NO to disable.
 */
- (void)setEnableCertificateValidation:(BOOL)enable;

3. Set the automatic failover threshold for primary/backup DNS

/** When both public cloud DNS and on-premises DNS are configured, this sets the number of primary DNS failures before automatically failing over to the backup DNS. If only one type of DNS is configured, do not call this method.
 *
 * Sets the number of primary DNS failures before automatically failing over to the backup DNS. If only one type of DNS is configured, do not call this method.
 * @param fallbackThreshold The number of failures (default is 4 when public cloud DNS is primary, 2 when on-premises DNS is primary).
 * The valid range is [0-4]. A value of 0 means failover occurs immediately. The maximum is 4.
 */
- (void)setFallbackThreshold:(NSInteger)fallbackThreshold;

FAQ

SDK/API related FAQ