iOS SDK development guide
Integrate the HTTPDNS iOS SDK into your app for reliable domain name resolution with built-in caching.
Overview
The iOS SDK encapsulates the DoH JSON API of HTTPDNS, providing APIs for domain name resolution in iOS apps and efficient domain name caching based on Time-to-Live (TTL) and Least Recently Used (LRU) policies. Integrate HTTPDNS into your iOS app to resolve domain name resolution anomalies with precise, low-cost resolution.
You can also set HTTPDNS as the default encrypted DNS resolver using the iOS 14 Native Encrypted DNS Solution.
Key benefits:
-
Easy to use:
Minimal code to integrate HTTPDNS into your app.
-
Zero latency:
An internal LRU cache stores resolved IPs locally and proactively refreshes expired entries, enabling zero-latency resolution.
Objective-C sample project: alidns_ios_demo sample project source code.
Swift sample project: DNSResolverSwiftDemo sample project source code.
SDK integration
Import the SDK
CocoaPods
-
In your Podfile, specify the repository location. Make sure to include the master repository.
source 'https://github.com/CocoaPods/Specs.git' source 'https://github.com/aliyun/aliyun-specs.git' -
Add the dependency for your project target:
pod 'AlicloudPDNS'
Manual
-
Download the iOS SDK from SDK Download.
-
After you obtain the SDK framework
pdns-sdk-ios.framework, manually add it to your project. -
Import the required system libraries:
-
Foundation.framework
-
SystemConfiguration.framework
-
CoreFoundation.framework
-
CoreTelephony.framework
-
-
In your project's Build Settings, add the -ObjC flag to Other Linker Flags.
SDK initialization
Register your application in the console to obtain its Account ID and AccessKey credentials before initialization.
To prevent resolution failures, initialize the SDK as early as possible in your application's lifecycle.
Initialize the SDK in the application:didFinishLaunchingWithOptions: method.
DNSResolver *resolver = [DNSResolver share];
// setAccountId:@"******": Replace the asterisks with the Account ID from the Access Configuration page in the console.
// andAccessKeyId:@"********": Replace the asterisks with the AccessKey ID of the key that you created on the Access Configuration page.
// andAccesskeySecret:@"********": Replace the asterisks with the AccessKey Secret of the key that you created on the Access Configuration page.
[resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
// Specify domains for automatic cache updates. The array can contain a maximum of 10 domains.
[resolver setKeepAliveDomains:@[@"your_domain_1", @"your_domain_2"]];
// Pre-resolve domains that you expect to use later.
[resolver preloadDomains:@[@"domain1", @"domain2", @"domain3"] complete:^{
// All domains are pre-resolved.
}];
API reference
Common settings
1. Account ID and authentication
Required. The console generates a unique Account ID when you register your app. Create an AccessKey pair to create an AccessKey for authentication, then configure it:
// setAccountId:@"******": Replace the asterisks with the Account ID from the Access Configuration page in the console.
// andAccessKeyId:@"********": Replace the asterisks with the AccessKey ID of the key that you created on the Access Configuration page.
// andAccesskeySecret:@"********": Replace the asterisks with the AccessKey Secret of the key that you created on the Access Configuration page.
[[DNSResolver share] setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
-
To prevent your Account ID, AccessKey ID, or AccessKey Secret from being exposed in logs or other data generated by your running app, disable SDK debug logging in your production release.
-
For demonstration purposes, the sample code passes the Account ID, AccessKey ID, and AccessKey Secret directly. These credentials are used for billing. To prevent them from being exposed through malicious decompilation, do not hardcode plaintext credentials in your production application. Instead, encode or encrypt them and decode or decrypt them only when needed. We also recommend that you obfuscate and harden your app's code. Otherwise, your Account ID, AccessKey ID, and AccessKey Secret may be compromised.
2. Set the resolution protocol
Set the resolution protocol (HTTP or HTTPS) using the scheme property.
The SDK uses and recommends the HTTPS protocol by default because it provides better security. HTTPDNS is billed based on the number of resolution requests. HTTPS requests are billed at five times the rate of HTTP requests. Select a scheme type based on your business needs. Set the property as follows:
[DNSResolver share].scheme = DNSResolverSchemeHttps;
3. Enable or disable caching
When enabled, resolved results are cached locally so subsequent lookups return instantly from cache.
Caching is enabled by default. To disable it, use the following code:
[DNSResolver share].cacheEnable=NO;
4. Set keep-alive caching for domains
When enabled, the SDK proactively refreshes cache entries for specified domains before they expire. This keeps data current but increases resolution requests and network traffic. Without this feature, cache updates occur only when a resolution method is called.
// The array can contain a maximum of 10 domains.
[[DNSResolver share] setKeepAliveDomains:@[@"www.taobao.com",@"www.aliyun.com"]];
-
Advantages:
-
Records are updated promptly (before the TTL expires).
-
When combined with pre-resolution, it can reduce initial resolution latency to 0 ms.
-
-
Disadvantage: Re-requesting at 75% of a record's TTL incurs additional costs.
5. Pre-resolution
Pre-resolve domains at app startup so subsequent lookups hit the cache with near-zero latency.
Code example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
DNSResolver *resolver = [DNSResolver share];
// setAccountId:@"******": Replace the asterisks with the Account ID from the Access Configuration page in the console.
// andAccessKeyId:@"********": Replace the asterisks with the AccessKey ID of the key that you created on the Access Configuration page.
// andAccesskeySecret:@"********": Replace the asterisks with the AccessKey Secret of the key that you created on the Access Configuration page.
[resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
resolver.cacheEnable = YES;
// Pre-resolve domains that you expect to use later.
[resolver preloadDomains:@[@"domain1", @"domain2", @"domain3"] complete:^{
// All domains are pre-resolved.
}];
return YES;
}
Advanced settings
1. Enable server-side IPv6 access
The HTTPDNS service supports dual-stack access (IPv4 and IPv6). By default, the SDK uses IPv4.
To use IPv6, ensure your network supports it and enable this feature:
[DNSResolver share].ipv6Enable = YES;
2. Short mode
The HTTPDNS DoH JSON API returns either full JSON or a simplified IP array. The SDK uses full JSON by default.
To use the simplified IP array format:
[DNSResolver share].shortEnable = YES;
3. Set the cache size
If caching is enabled, you can customize the number of domains to cache. The supported range is 100 to 500.
The default cache size is 100 domains. To set a custom cache size, use the cacheCountLimit property:
[DNSResolver share].cacheCountLimit = 200;
4. Enable or disable IP speed test
When the IP speed test is enabled, resolution results are sorted by latency, with the fastest IP returned first.
The IP speed test is disabled by default. To enable it, use the following code:
[DNSResolver share].speedTestEnable=YES;
5. Set the IP speed test method
Configure the probing method: set to 0 for ICMP probing, or to a port number (80, 443, etc.) for socket probing on that port.
The default value is 443.
[DNSResolver share].speedPort = 80;
6. Enable or disable ISP-specific domain name caching
When enabled, cache entries are isolated per ISP network. When disabled, all networks share a single cache.
ISP-specific domain name caching is enabled by default.
[DNSResolver share].ispEnable = YES;
7. Set the maximum TTL for the negative cache
Set a maximum TTL for the negative cache, which stores results for failed lookups.
The default value is 30s. To set a custom maximum TTL for the negative cache, use the following code:
[DNSResolver share].maxNegativeCache = 30;
8. Set the maximum cache TTL
Set a maximum cache TTL to cap entry lifetimes regardless of server-provided TTL values.
The default value is 3600s. To set a custom maximum cache TTL, use the following code:
[DNSResolver share].maxCacheTTL= 3600;
9. Enable immutable cache
[DNSResolver share].immutableCacheEnable = NO;// Immutable cache is disabled by default.
The SDK provides three cache update mechanisms:
-
Immutable cache: When this feature is enabled, the cache is treated as always valid during the app's runtime. The SDK skips expiration checks and updates, which minimizes the number of resolution requests.
To enable it, call:
[DNSResolver share].immutableCacheEnable = YES -
Active cache update: This helps ensure that resolution requests use the most up-to-date cache. When a domain's authoritative record changes, this mechanism allows requests to benefit from low-latency cache hits while ensuring the cached records are current. The array is limited to a maximum of 10 domains.
To enable it, call:
[[DNSResolver share] setKeepAliveDomains:@[@"your_domain_1",@"your_domain_2"]] -
Passive cache update: The cache is updated passively when you call either of the following two methods to get resolution results:
-
The
- (void)getIpv4DataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))completemethod gets an array of IPv4 addresses for a domain. If the cache is not empty and the entry is within its TTL, the method returns the cached result immediately. Otherwise, it fetches the latest result over the network, returns it, and updates the cache. Use this method in scenarios that require high accuracy. -
The
- (NSArray<NSString *> *)getIpv4ByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enablemethod retrieves IPv4 resolution results from the cache. This method's behavior depends on theenableparameter.Parameter description: If
enableisYES, the method returns a stale record even if the cache is expired (ornilif the cache is empty) and then asynchronously updates the cache. IfNO, it returnsnilwhen the cache is expired or empty and then asynchronously updates the cache.
-
10. Timeout
The timeout property sets the request timeout for resolution. Default: 3s. Recommended range: 2s to 5s.
Service APIs
Code example:
/// Pre-resolves domain information. Call this method at app startup. The results are stored in the cache to accelerate subsequent resolutions.
/// Automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack) to get an IP suitable for the current network.
/// @param domainArray An array of domain names.
/// @param complete A callback that is executed after resolution is complete.
- (void)preloadDomains:(NSArray<NSString *> *)domainArray complete:(void(^)(void))complete;
/// Gets an array of IP addresses for a domain. This method automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack) to get a suitable IP.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IP over the network. If caching is disabled, it always fetches the IP over the network.
/// @param domain The domain name.
/// @param complete A callback that returns all IP addresses.
- (void)getIpsDataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))complete;
/// Gets an array of IP addresses directly from the cache without waiting. This method automatically detects the network environment (IPv4-only, IPv6-only, or dual-stack).
/// Returns nil if the cache is empty. If the cache is not empty and `enable` is set to YES, it returns the cached data. If the data is expired, it asynchronously updates the cache. If the cache is not empty and `enable` is set to NO, it returns nil for expired entries and asynchronously updates the cache.
/// @param domain The domain name.
/// @param enable Specifies whether to return expired IP addresses.
- (NSArray<NSString *> *)getIpsByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable;
/// Gets an array of IPv4 information objects for a domain.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IPs over the network. If caching is disabled, it always fetches the IPs over the network.
/// @param domain The domain name.
/// @param complete A callback that returns all domain information objects.
- (void)getIpv4InfoWithDomain:(NSString *)domain complete:(void(^)(NSArray<DNSDomainInfo *> *domainInfoArray))complete;
/// Gets an array of IPv6 information objects for a domain.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IPs over the network. If caching is disabled, it always fetches the IPs over the network.
/// @param domain The domain name.
/// @param complete A callback that returns all domain information objects.
- (void)getIpv6InfoWithDomain:(NSString *)domain complete:(void(^)(NSArray<DNSDomainInfo *> *domainInfoArray))complete;
/// Gets a random IPv4 information object for a domain.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IP over the network. If caching is disabled, it always fetches the IP over the network.
/// @param domain The domain name.
/// @param complete A callback that returns one random domain information object.
- (void)getRandomIpv4InfoWithDomain:(NSString *)domain complete:(void(^)(DNSDomainInfo *domainInfo))complete;
/// Gets a random IPv6 information object for a domain.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IP over the network. If caching is disabled, it always fetches the IP over the network.
/// @param domain The domain name.
/// @param complete A callback that returns one random domain information object.
- (void)getRandomIpv6InfoWithDomain:(NSString *)domain complete:(void(^)(DNSDomainInfo *domainInfo))complete;
/// Gets an array of IPv4 addresses for a domain.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IPs over the network. If caching is disabled, it always fetches the IPs over the network.
/// @param domain The domain name.
/// @param complete A callback that returns all IP addresses.
- (void)getIpv4DataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))complete;
/// Gets an array of IPv6 addresses for a domain.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IPs over the network. If caching is disabled, it always fetches the IPs over the network.
/// @param domain The domain name.
/// @param complete A callback that returns all IP addresses.
- (void)getIpv6DataWithDomain:(NSString *)domain complete:(void(^)(NSArray<NSString *> *dataArray))complete;
/// Gets a random IPv4 address for a domain.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IP over the network. If caching is disabled, it always fetches the IP over the network.
/// @param domain The domain name.
/// @param complete A callback that returns one random IP address.
- (void)getRandomIpv4DataWithDomain:(NSString *)domain complete:(void(^)(NSString *data))complete;
/// Gets a random IPv6 address for a domain.
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IP over the network. If caching is disabled, it always fetches the IP over the network.
/// @param domain The domain name.
/// @param complete A callback that returns one random IP address.
- (void)getRandomIpv6DataWithDomain:(NSString *)domain complete:(void(^)(NSString *data))complete;
/// Pre-resolves IPv4 information for domains. Call this at app startup. The results are stored in the cache to accelerate subsequent resolutions.
/// @param domainArray An array of domain names.
/// @param complete A callback that is executed after resolution is complete.
- (void)preloadIpv4Domains:(NSArray<NSString *> *)domainArray complete:(void(^)(void))complete;
/// Pre-resolves IPv6 information for domains. Call this at app startup. The results are stored in the cache to accelerate subsequent resolutions.
/// @param domainArray An array of domain names.
/// @param complete A callback that is executed after resolution is complete.
- (void)preloadIpv6Domains:(NSArray<NSString *> *)domainArray complete:(void(^)(void))complete;
/// Gets an array of IPv4 addresses directly from the cache without waiting.
/// Returns nil if the cache is empty. If the cache is not empty and `enable` is set to YES, it returns the cached data. If the data is expired, it asynchronously updates the cache. If the cache is not empty and `enable` is set to NO, it returns nil for expired entries and asynchronously updates the cache.
/// @param domain The domain name.
/// @param enable Specifies whether to return expired IP addresses.
- (NSArray<NSString *> *)getIpv4ByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable;
/// Gets an array of IPv6 addresses directly from the cache without waiting.
/// Returns nil if the cache is empty. If the cache is not empty and `enable` is set to YES, it returns the cached data. If the data is expired, it asynchronously updates the cache. If the cache is not empty and `enable` is set to NO, it returns nil for expired entries and asynchronously updates the cache.
/// @param domain The domain name.
/// @param enable Specifies whether to return expired IP addresses.
- (NSArray<NSString *> *)getIpv6ByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable;
/// Collects request statistics.
-(NSArray *)getRequestReportInfo;
API usage examples
1. Set basic information
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Initialization method
DNSResolver *resolver = [DNSResolver share];
// setAccountId:@"******": Replace the asterisks with the Account ID from the Access Configuration page in the console.
// andAccessKeyId:@"********": Replace the asterisks with the AccessKey ID of the key that you created on the Access Configuration page.
// andAccesskeySecret:@"********": Replace the asterisks with the AccessKey Secret of the key that you created on the Access Configuration page.
[resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
// Specify domains for automatic cache updates. The array can contain a maximum of 10 domains.
[resolver setKeepAliveDomains:@[@"your_domain_1",@"your_domain_2"]];
// Pre-resolve domains to get resolution results in advance and store them in the cache.
[resolver preloadDomains:@[@"domain1", @"domain2", @"domain3"] complete:^{
// All domains are pre-resolved.
}];
return YES;
}
2. Use the domain name resolution API
The SDK provides multiple resolution methods in the DNSResolver.h header file. The following example shows a method that automatically adapts to the network environment (IPv4-only, IPv6-only, or dual-stack).
API declaration:
/// Gets an array of IP addresses for a domain. This method automatically adapts to the network environment (IPv4-only, IPv6-only, or dual-stack).
/// If caching is enabled, it returns data from the cache first. If the cache is empty or expired, it fetches the IPs over the network. If caching is disabled, it always fetches the IPs over the network.
/// @param domain The domain name.
/// @param complete A callback that returns 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 www.taobao.com.
if (dataArray.count > 0) {
//TODO: Use an IP address for the URL connection.
}
}];
3. Get resolution results directly from the cache
API declaration:
/// Gets an array of IP addresses directly from the cache without waiting. This method automatically adapts to the network environment (IPv4-only, IPv6-only, or dual-stack).
/// Returns nil if the cache is empty. If the cache is not empty and `enable` is set to YES, it returns the cached data. If the data is expired, it asynchronously updates the cache. If the cache is not empty and `enable` is set to NO, it returns nil for expired entries and asynchronously updates the cache.
/// @param domain The domain name.
/// @param enable Specifies whether to return expired IP addresses.
- (NSArray<NSString *> *)getIpsByCacheWithDomain:(NSString *)domain andExpiredIPEnabled:(BOOL)enable;
Call example:
NSArray *result = [[DNSResolver share] getIpsByCacheWithDomain:@"your_domain" andExpiredIPEnabled:YES];
// Use the cached result.
if (result.count > 0) {
//TODO: Use an IP address for the URL connection.
}
Note: Retrieving results directly from the cache is fast, but the method returns nil if the cache is empty, or if the cache is expired and enable is set to NO.
4. Clear the cache
API declaration:
/// `hostArray` is an array of hostnames to clear from the cache. To clear all data, pass nil or an empty array.
-(void)clearHostCache:(NSArray <NSString *>*)hostArray;
Call example:
[[DNSResolver share] clearHostCache:@[@"domain1", @"domain2"]];
5. Collect statistics
API declaration:
/// Collects request statistics.
-(NSArray *)getRequestReportInfo;
Call example:
NSArray *array = [[DNSResolver share] getRequestReportInfo];
Data format:
(
{
avgRtt = "1"; // Average domain resolution time (ms)
cacheDnsNum = 0; // Number of cache hits
domain = "www.taobao.com"; // The resolved domain
gobackLocaldnsNum = 0; // Number of fallbacks to local DNS
localErro = 0; // Number of local DNS resolution failures
maxRtt = "60"; // Maximum domain resolution time (ms)
noPermissionErro = 0; // Number of authentication failures
noResponseErro = 0; // Number of no-response errors
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 for IPv4, 28 for IPv6
urlParameterErro = 0; // Number of URL parameter format errors
urlPathErro = 0; // Number of URL path errors
}
......
);
Best practices for domain name resolution
Combine pre-resolution with stale-while-revalidate for optimal performance.
Pre-resolving key domains at startup populates the cache so subsequent lookups avoid network round-trips, achieving near-zero-latency resolution.
The built-in cache then serves subsequent requests directly from memory, further enhancing the user experience.
1. Pre-resolution
Enable caching and pre-resolve key domains at app startup.
In the AppDelegate's application:didFinishLaunchingWithOptions: method, pre-resolve the domain names used in your application, and cache the results in local memory.
1. For IPv4-only scenarios
//******** For IPv4-only scenarios *******
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
DNSResolver *resolver = [DNSResolver share];
// setAccountId:@"******": Replace the asterisks with the Account ID from the Access Configuration page in the console.
// andAccessKeyId:@"********": Replace the asterisks with the AccessKey ID of the key that you created on the Access Configuration page.
// andAccesskeySecret:@"********": Replace the asterisks with the AccessKey Secret of the key that you created on the Access Configuration page.
[resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
// Enable caching. Default is YES.
resolver.cacheEnable = YES;
// Pre-resolve IPv4 information for domains that you expect to use later.
[resolver preloadIpv4Domains:@[@"domain1", @"domain2", @"domain3"] complete:^{
// All domains are pre-resolved.
}];
return YES;
}
2. For IPv6 scenarios
//******** For dual-stack scenarios that support IPv6 *******
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
DNSResolver *resolver = [DNSResolver share];
// setAccountId:@"******": Replace the asterisks with the Account ID from the Access Configuration page in the console.
// andAccessKeyId:@"********": Replace the asterisks with the AccessKey ID of the key that you created on the Access Configuration page.
// andAccesskeySecret:@"********": Replace the asterisks with the AccessKey Secret of the key that you created on the Access Configuration page.
[resolver setAccountId:@"******" andAccessKeyId:@"********" andAccesskeySecret:@"********"];
// Enable caching. Default is YES.
resolver.cacheEnable = YES;
// Enable domain resolution over IPv6 networks. Default is NO.
resolver.ipv6Enable = YES;
// Enable IP speed test. Default is NO.
resolver.speedTestEnable = YES;
// Pre-resolve domains. This automatically detects the network (IPv4-only, IPv6-only, or dual-stack) and gets a suitable IP.
[resolver preloadDomains:@[@"domain1", @"domain2", @"domain3"] complete:^{
// All domains are pre-resolved.
}];
return YES;
}
2. Allow stale responses
Before making a network request, retrieve the IP from the cache with expired records allowed (andExpiredIPEnabled:YES). This returns a result immediately as long as the cache is populated, even if the TTL has expired, achieving zero-latency resolution.
1. For IPv4-only scenarios
//******** For IPv4-only scenarios *******
__weak typeof(self) ws = self;
// Prioritize getting the IP from the cache, allowing stale but usable records.
NSArray<NSString *> *cachedIPs = [[DNSResolver share] getIpv4ByCacheWithDomain:domain andExpiredIPEnabled:YES];
if (cachedIPs && cachedIPs.count > 0) {
NSString *ip = cachedIPs.firstObject;
NSLog(@"Fast cache hit. Domain %@ resolved to IP: %@", domain, ip);
[self requestWithIP:ip domain:domain]; // Connect directly using the IP.
} else {
// Cache miss. Perform an asynchronous resolution.
[[DNSResolver share] getIpv4DataWithDomain:domain complete:^(NSArray<NSString *> *resolvedIPs) {
if (resolvedIPs && resolvedIPs.count > 0) {
NSString *ip = resolvedIPs.firstObject;
NSLog(@"Async resolution complete. Domain %@ resolved to IP: %@", domain, ip);
[ws requestWithIP:ip domain:domain]; // Connect directly using the IP.
} else {
NSLog(@"Domain resolution failed. Falling back to original domain.");
[ws requestWithIP:domain domain:domain]; // Fallback: Use the original domain.
}
}];
}
2. For IPv6 scenarios
//******** For dual-stack scenarios that support IPv6 *******
__weak typeof(self) ws = self;
// Prioritize getting the IP from the cache, allowing stale but usable records.
NSArray<NSString *> *cachedIPs = [[DNSResolver share] getIpsByCacheWithDomain:domain andExpiredIPEnabled:YES];
if (cachedIPs && cachedIPs.count > 0) {
NSString *ip = cachedIPs.firstObject;
NSLog(@"Fast cache hit. Domain %@ resolved to IP: %@", domain, ip);
[self requestWithIP:ip domain:domain]; // Connect directly using the IP.
} else {
// Cache miss. Perform an asynchronous resolution.
[[DNSResolver share] getIpsDataWithDomain:domain complete:^(NSArray<NSString *> *resolvedIPs) {
if (resolvedIPs && resolvedIPs.count > 0) {
NSString *ip = resolvedIPs.firstObject;
NSLog(@"Async resolution complete. Domain %@ resolved to IP: %@", domain, ip);
[ws requestWithIP:ip domain:domain]; // Connect directly using the IP.
} else {
NSLog(@"Domain resolution failed. Falling back to original domain.");
[ws requestWithIP:domain domain:domain]; // Fallback: Use the original domain.
}
}];
}
Usage notes
-
The
pdns-sdk-ios.frameworkrequires a minimum iOS version of 9.0. -
When you make requests over HTTP, you must set
App Transport Security Settings->Allow Arbitrary LoadstoYESin yourInfo.plistfile. -
After resolving an IP from HTTPDNS, set the
HostHTTP header to the original domain name when making service requests.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" forHTTPHeaderField:@"host"]; -
Implement a fallback mechanism: if the SDK returns no IP, make the request using the original domain name.
NSArray *array = [[DNSResolver share] getIpsByCacheWithDomain:@"original_domain" andExpiredIPEnabled:YES]; if (array && array.count > 0 && array.firstObject.length > 0) { // Replace the host in the URL with the IP and make the request. } else { // Implement fallback logic (use the original URL for the request). } -
When an intermediate HTTP proxy is present, a client request uses an absolute URL in its request line. When you enable HTTPDNS and use an IP URL, the intermediate proxy identifies the IP address from the URL and passes it to the target server as the Host information. As a result, the target server cannot process HTTP requests that lack the actual Host information. We recommend that you check whether a network proxy is enabled on your device. If your device is in proxy mode, 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.
On-premises DNS suits scenarios with strict data compliance and custom resolution policies, such as finance, government, or large enterprises. The SDK supports four deployment modes: public cloud only, on-premises DNS only, and two primary/backup hybrid modes where public cloud and on-premises DNS serve as mutual fallbacks.
Core features
-
Private deployment support: Configure on-premises DNS server endpoints using IPv4/IPv6 addresses or hostnames.
-
Mutual authentication: Use your dedicated
accessKeyIdandaccessKeySecretto sign requests and secure communication. -
Circuit breaking and health checks: If an on-premises DNS node fails three or more consecutive times, circuit breaking is automatically triggered. Afterward, the system checks its availability every minute by using the specified
healthCheckDomain. The node is automatically re-enabled after it recovers. -
Certificate validation control: Enable or disable TLS certificate validation. We strongly recommend enabling it in production environments.
-
Smart failover: Automatically fails over to the backup DNS when the primary DNS (public cloud or on-premises) reaches a failure threshold, ensuring high availability for resolution.
-
Seamless API compatibility: Resolution API calls remain the same whether you use public cloud DNS or on-premises DNS, requiring no changes to your business logic.
Configuration examples
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 primary, on-premises backup
If the primary Alibaba Cloud public HTTPDNS fails, resolution 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 primary, public cloud backup
If the primary on-premises DNS fails, resolution 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
The SDK provides three core APIs for on-premises DNS configuration, security policy control, and automatic primary/backup failover.
1. On-premises DNS configuration
/** 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.
* Use this interface to pass your private DNS server's address and authentication credentials.
* 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 hostnames (can be nil).
* @param port The service port (for example, @"443"). If nil, the default port is used.
* @param healthCheckDomain The domain for health checks after a circuit break. If a resolution service fails more than three consecutive times, the circuit is tripped, and the service IP enters a healthCheck state. Subsequent requests will not use this service. A timer then probes this healthCheckDomain every minute to check if the service is available. If a probe succeeds, the service state is restored to alive and can receive requests again.
* @param accessKeyId Your private accessKeyId for authentication.
* @param accesskeySecret Your 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. TLS certificate validation
/** This is related to on-premises DNS for private deployments. Do not call this method if you only use public DNS.
*
* Enable or disable certificate validation for on-premises DNS. The default is YES. If your server is not configured with a domain or IP certificate, you can set this to NO for testing. For production environments, set this to YES to avoid security risks.
* @param enable YES to enable (default), NO to disable.
*/
- (void)setEnableCertificateValidation:(BOOL)enable;
3. Automatic failover threshold
/** When both public cloud DNS and on-premises DNS are configured, this sets the number of primary DNS failures that trigger an automatic failover 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. The default is 4 when public cloud DNS is primary, and 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;