Programmatically connecting your Xamarin.iOS app to a wifi network using NEHotspotConfigurationManager

At Xamarin.University we offer office hours for our students. These are 30 minute sessions where we trainers discuss topics that are beyond the scope of our classes. Often, the questions are about a very specific topic and sometimes the initial reaction is something like “No, that’s not possible.” – but then we start doing some research and the mood is changing into “Well, who would have thought this…it is indeed possible!” My last OH was about one of these surprising topics and I’d like to share my findings with you.

“How can I let my app scan for available wifi networks and how can it join one of them?”

Let’s split this into to separate topics and the TL;DR is: scanning isn’t possible (easily) but joining a network is possible. Please note that everything I discuss here will only work on a physical device and with iOS11 or later unless explicitly mentioned.

Scanning for available networks

As already mentioned, there is no easy way of scanning for available networks. iOS does not have a general purpose API to do this – or at least it does not have a public API. However, your app might be able to use one of the special purpose APIs but only if you explicitly request an exception from Apple and can explain why your app needs it. You can read some more background information in this document which is a good entry point to the relevant NEHotspotHelper class.

The document to gain access to the APIs can be found in Apple’s developer portal. You’ll have to fill in a form and submit it.

Hotspot Helper

Request Hotspot Helper access

I have no experience with NEHotspotHelper and thus cannot provide further advice – from the discussion in the office hour I learned that Apple does not make it easy and really needs to be convinced. Hotspot helper functionality is not new in iOS11 but has been there since iOS9.

Joining a network

Let’s assume you know the name (SSID) of an existing network and would like your app to join it. Maybe your app is meant to communicate with a specific piece of hardware. So, can it be done? Yes, it can and iOS11+ offers a very elegant way of doing it. But let’s first go one step back. Prior to iOS8 the official way to make users join a specific network was to instruct them to open the Settings app, navigate to the wifi setup and manually connect to network and then come back to the app. Is this user friendly? Nope. So Apple improved it a bit. Beginning with iOS8, your app can redirect the user to the settings app:

bool success = await UIApplication.SharedApplication.OpenUrlAsync(
                NSUrl.FromString(UIApplication.OpenSettingsUrlString), options: new UIApplicationOpenUrlOptions());

Above code will open the settings section of your app inside of the Settings app. As of iOS11 it is no longer possible to navigate to a specific submenu of the settings. Identifiers like “app-settings:root=WIFI” no longer work or will just open settings. This solutions still isn’t optimal because it does not take the user to the exact right location. But wait! There’s more!

Introducing NEHotspotConfigurationManager

This is new with iOS11+ and enables your app to join a network using a known SSID. You can find the documentation over at Apple. To use it, you won’t have to request access to the super-secret hotspot APIs but there’s still a little configuration required. The first step is to set the “Enable Hotspot Configuration” entitlement in your Entitlements.plist file:

Enable hotspot entitlement

This entitlement must also be configured in the developer portal when creating an app ID for your application:

Enable Hotspot service in developer portal

With these settings in place you can start connecting to a network. The code is not complicated and requires the creation of an NEHotspotConfiguration object which specifies how the network will be used. Among the configurable parameters is for example a boolean that tells iOS if you are trying to access the network just temporarily or for longer. You should read the documentation to find out what settings work best for your case. Here’s a code example:

var config = new NEHotspotConfiguration(ssid, password, isWep: false);
config.JoinOnce = false;

var tcs = new TaskCompletionSource<NSError>();
NEHotspotConfigurationManager.SharedManager.ApplyConfiguration(config, err => tcs.SetResult(err));

var error = await tcs.Task;
if (error != null)
{
    var alert = new UIAlertController
    {
        Title = "Error",
        Message = error.Description
    };
    alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
    PresentViewController(alert, true, null);
    return;
}

Using the singleton NEHotspotConfigurationManager.SharedManager you can apply (activate) a configuration. If you use Intellisense in Visual Studio, you will see an async version of ApplyConfiguration(). However, the binding is not correct. It returns a Task but should return a Task<bool>. That’s why I’m using the non-async version and add a TaskCompletionSource. Applying a configuration will bring up a dialog asking for user interaction (you cannot customize or avoid this dialog!). If the user confirms, the joined network will show up in the list of wifi networks inside of the Settings app. The status bar of the device will show the wifi icon.

To test, either create your own app or (“give me da cod3z!”) check out the git repo at https://github.com/Krumelur/WifiDemo

Then:

  • Create your own app ID and set the service requirements correctly
  • Deploy the app to a physical device (it won’t work on a simulator!)
  • If you have only one wifi network, make iOS forget the network in the Settings app
  • Run the app and enter your SSID and password and watch the magic happen

As always, I’m hoping somebody will find this information useful.

Leave a Reply

Your email address will not be published. Required fields are marked *