With the popularity and rise of IoT applications you might get faced with requirement that your iOS application should be able to connect to a Wi-Fi device. Since iOS11 was released we got access to new APIs that allows us to do just that.
In this article I will show you an easy way how to connect to a Wi-Fi device from iOS application with some “gotchas” along the way.
Requirements
If we want to connect to Wi-Fi device we need to meet next requirements:
- iOS11 SDK or later
- Add Hotspot Configuration Entitlement to our project.
How?
To actually connect / move our device to another Wi-Fi we need to know at least SSID and password of target network if it is protected by password.
For actual implementation we will use NEHotspotConfigurationManager and NEHotspotConfiguration which are available from iOS11 and later.
1. Define configuration
First we need to define configuration of the hotspot. There are many other implementations and configurations available. To keep things simple and short I will show you just one of them.
You need to initialise NEHotspotConfiguration with SSID, passphrase and boolean indicating if network is WEP. For networks that are WPA/WPA2 you should pass ‘false’ value.
let hotspotConfiguration = NEHotspotConfiguration(
ssid: "anSSID",
passphrase: "aPassword",
isWEP: false
)
Configiration itself has also some additional properties that we can set. One that I find pretty important is ‘joinOnce’.
hotspotConfiguration.joinOnce = true
Setting ‘joinOnce’ to true will cause system to disconnect from the target network when your application will go to background for more than 15s, application crashes, user connects to another Wi-Fi etc. Based on the use case consider setting this property to avoid any unexpected behaviour.
IMPORTANT UPDATE FOR iOS15 – since release of iOS15 it appears that ‘joinOnce’ has issues when trying to join the network. If we set this value as true, it appears that our apps are can not join the network or sometimes disconnect from it. For iOS15 I suggest setting this value to false and manually handle disconnections. If you have any issues feel free to contact me. There is also updated implementation of HotspotClient iOS framework on my GitHub.
2. Apply configuration
To actually move iOS device to target network we need to apply configuration to the system. As a result, our device will switch to target network if no errors will occur.
let hotspotManager = NEHotspotConfigurationManager.shared
hotspotManager.apply(hotspotConfiguration) { error in
if let error = error {
print(error)
}
}
After applying configuration an error can occur. As usual error have codes. Codes that are related to this error as defined as NEHotspotConfigrationError.
public enum NEHotspotConfigurationError : Int {
case invalid = 0
case invalidSSID = 1
case invalidWPAPassphrase = 2
case invalidWEPPassphrase = 3
case invalidEAPSettings = 4
case invalidHS20Settings = 5
case invalidHS20DomainName = 6
case userDenied = 7
case `internal` = 8
case pending = 9
case systemConfiguration = 10
case unknown = 11
case joinOnceNotSupported = 12
case alreadyAssociated = 13
case applicationIsNotInForeground = 14
case invalidSSIDPrefix = 15
}
Two errors that I find pretty important are ‘userDenied’ and ‘alreadyAssociated’.
Triggering apply configuration also triggers UI prompt to the user if he allows our app to join specific network – the one we are joining with configuration. If user denies connection, you will receive ‘userDenied’ error.
Another important one is ‘alreadyAssociated’. It is interesting that API doesn’t deliver standard Swift Result type but optional error instead. It is a bit weird in this case. In case that our device is already on target network you will get ‘alreadyAssociated’ since this case is defined as error and not as an success, which might be a bit more logical for someone.
If no error occurred you are pretty much finished and sure that you are connected Wi-Fi device and you can start communicate with the device.
But there is one gotcha which I will describe at the end of the article.
3. Disconnect
Disconnecting from Wi-Fi is pretty straight forward tasks. All need to do is remove configuration.
hotspotManager.removeConfiguration(forSSID: "anSSID")
Gotcha bonus
Remember when I said that there is one gotcha when connecting to Wi-Fi devices?
Well actually after completion block from gets invoked there is no guarantee that our device is actually at target network. And if your app relies on that you might be faces with some bugs or issues.
hotspotManager.apply(hotspotConfiguration) { error in
/* If no error occurred, we would assume that we are at target network already"
}
Instead what happens is that system only tells us that it only has successfully applied hotspot configuration. Which doesn’t equally mean that it actually also already connected.
So you might question yourself how can we be then sure when device actually joined network?
Luckily we have an API for retrieving current network information. There are some limitations regarding when it actually delivers result and when it doesn’t. But in our case, it does, since user allowed our app to configure network.
So if no error occurred we can simply check for the current network and compare it against configuration.
Super simple version of the implementation would be something like this.
hotspotManager.apply(hotspotConfiguration) { error in
/ * Configuration applied */
if error = nil {
NEHotspotNetwork.fetchCurrent { network in
if network?.ssid == configuration.ssid {
/* Successfully joined */
}
}
}
}
I highly suggest that you add some delays or even retries for re-fetching of current networks since we live in asynchronous world and sometimes things take a bit longer.
Learnings and result
We have covered some of the basics approaches how to connect to a specific Wi-Fi from our application. After reading this I hope that you are able to:
- connect to simple Wi-Fi network
- disconnect Wi-Fi network.
- validate that you are actually on desired network
If you are interested in how this solution would look like in production you are more than welcome to check my project on my GitHub. It is a simple HotspotClient iOS framework which you are more than welcome to use.
References:
- https://developer.apple.com/documentation/networkextension/nehotspotconfigurationmanager
- https://developer.apple.com/documentation/networkextension/nehotspotconfigurationmanager
- https://developer.apple.com/documentation/networkextension/nehotspotnetwork
In case of any questions or comments feel free to contact me or leave it in the comments section bellow.
Architectural Patterns – Decorator
In this article I will present you powerful architectural pattern that I have been using it a lot called Decorator.
1. What is Decorator
...Detect user’s country without accessing their location
Sometimes we are faced with challenge where we would like to improve user experience of our app based on where in the world or better said in which country ...