I want to change/add DNS server for connected WiFi of my iPhone device in iOS programmatically?
The Google Public DNS IP addresses (IPv4) are as follows: 8.8.8.8 8.8
Look at the Network Extensions classes. With the NETunnelProviderManager class you can set the onDemandRules with a NEEvaluateConnectionRule. The NEEvaluateConnectionRule constructor can take a list of wildcard with top-level-domains (i.e. ["*.com", "*.net", "*.org", "*.io"]
) as the domains, and use NEEvaluateConnectionRuleAction.connectIfNeeded as the action. Set the onDemandRules of the NEEvaluateConnectionRule you create with all tlds as the domains. Then create a NEOnDemandRuleEvaluateConnection and set its connectionRules to the NEEvaluateConnectionRule created with all of the top-level-domains, and set its interfaceTypeMatch to NEOnDemandRuleInterfaceType.any. Set the NETunnelProviderManager.onDemandRules with a NEOnDemandRuleEvaluateConnection created this way. If you create a NETunnelProviderManagerand load it and save it as described above, you can then turn it on and off by using the NETunnelProviderManager.isEnabled and NETunnelProviderManager.isOnDemandEnabled properties.
Here is an example class that does exactly that.
import Foundation
import NetworkExtension
public class VPNConnect {
private static let vpnDescription = "DNS OnDemand to GoogleDNS"
private static let vpnServerDescription = "OnDemand DNS to GoogleDNS"
public var manager:NETunnelProviderManager = NETunnelProviderManager()
public var dnsEndpoint1:String = "8.8.8.8"
public var dnsEndpoint2:String = "8.8.4.4"
public var connected:Bool {
get {
return self.manager.isOnDemandEnabled
}
set {
if newValue != self.connected {
update(
body: {
self.manager.isEnabled = newValue
self.manager.isOnDemandEnabled = newValue
},
complete: {
if newValue {
do {
try (self.manager.connection as? NETunnelProviderSession)?.startVPNTunnel(options: nil)
} catch let err as NSError {
NSLog("\(err.localizedDescription)")
}
} else {
(self.manager.connection as? NETunnelProviderSession)?.stopVPNTunnel()
}
}
)
}
}
}
public init() {
refreshManager()
}
public func refreshManager() -> Void {
NETunnelProviderManager.loadAllFromPreferences(completionHandler: { (managers, error) in
if nil == error {
if let managers = managers {
for manager in managers {
if manager.localizedDescription == VPNConnect.vpnDescription {
self.manager = manager
return
}
}
}
}
self.setPreferences()
})
}
private func update(body: @escaping ()->Void, complete: @escaping ()->Void) {
manager.loadFromPreferences { error in
if (error != nil) {
NSLog("Load error: \(String(describing: error?.localizedDescription))")
return
}
body()
self.manager.saveToPreferences { (error) in
if nil != error {
NSLog("vpn_connect: save error \(error!)")
} else {
complete()
}
}
}
}
private func setPreferences() {
self.manager.localizedDescription = VPNConnect.vpnDescription
let proto = NETunnelProviderProtocol()
proto.providerBundleIdentifier = "com.popmedic.vpntunnel.provider"
proto.serverAddress = VPNConnect.vpnServerDescription
self.manager.protocolConfiguration = proto
// TLDList is a struct I created in its own swift file that has an array of all top level domains
let evaluationRule = NEEvaluateConnectionRule(matchDomains: TLDList.tlds,
andAction: NEEvaluateConnectionRuleAction.connectIfNeeded)
evaluationRule.useDNSServers = [self.dnsEndpoint1, self.dnsEndpoint2]
let onDemandRule = NEOnDemandRuleEvaluateConnection()
onDemandRule.connectionRules = [evaluationRule]
onDemandRule.interfaceTypeMatch = NEOnDemandRuleInterfaceType.any
self.manager.onDemandRules = [onDemandRule]
}
}
Please note that you will have to turn the Network Extensions capabilities on and there will be a dialog presented telling the user that you are turning on a VPN connection, but you will not have the [VPN] icon in the status bar when the connection is turned on because we are not setting a vpn, just using the on demand rules.
Here is the TLDList class I used.
Hate Google as much as I do, maybe use this for the DNS you set... Quad9