Working with NEHotspotHelper

I had to evaluate NEHotspotHelper for a project I am working on. The documentation from Apple is sordid to say the least. Here are a few bits and pieces of information that will help you understand the API better.

Apply for the Network Extension Entitlement

You may already know this but to use the NEHotspotHelper API you will need to apply for the Network Extension Entitlement from Apple. It may take several days for Apple to approve or reject your application.

Modify your Provisioning Profiles

Once you are granted the entitlement, go to iOS developer console and add the entitlement to your developer and deployment provisioning profiles. Many on the Internet will tell you to create a new set of provisioning profiles. That is not necessary. You can add entitlements to existing profiles. Download these modified profiles and re-install them on your development machine.

Add Background Mode

In our case we wanted to use the Wi-Fi Hotspot Authentication API of NEHotspotHelper. This requires the app to run in the background mode. Edit your Info.plist file and add the network-authentication background mode.

Use the Wi-Fi Hotspot Authentication API

The purpose of the authentication API is to implement custom application level logic to authenticate the connection to a WIFI hotspot. This authentication is a two step process:

  1. First connect to the hotspot providing the hotspot’s password.
  2. Then perform custom authentication. For example, this can involve communicating with a server and passing additional credentials.

This process uses a callback based approach. The first thing you do is register a closure or lambda that is invoked at various times with a command object.

let options: [String: NSObject] = [kNEHotspotHelperOptionDisplayName : "Join this WIFI" as NSObject]
let queue: DispatchQueue = DispatchQueue(label: "com.mobiarch", attributes: DispatchQueue.Attributes.concurrent)

NSLog("Started wifi scanning.")

NEHotspotHelper.register(options: options, queue: queue) { (cmd: NEHotspotHelperCommand) in
  NSLog("Received command: \(cmd.commandType.rawValue)")

There are three command types that you need to be aware of to perform custom authentication.

  • NEHotspotHelperCommandType.filterScanList – iOS will call your closure with this command type when the user opens the WIFI Settings page. You can obtain a list of all available hotspots from the command. Your code needs to create a filtered list of hotspots that you will like to authenticate with. You also need to set the WIFI password for these hotspots at this point. Once the closure returns, iOS will show the display name below the filtered hotspots. The display name is set using the kNEHotspotHelperOptionDisplayName option as shown in the code above. Note: Filtering does not really remove other hotspots from the list in the WIFI settings page. It is simply meant to identify the networks that your app deems appropriate for authentication.
  • NEHotspotHelperCommandType.evaluate – If the user taps on one of your filtered hotspots and makes a connection the closure is called again with an evaluate command. You can perform custom business logic to evaluate the connected hotspot. If it is appropriate, your code should set a high confidence level for the network.
  • NEHotspotHelperCommandType.authenticate – After the evaluation phase the closure is called again with an authenticate command. Your code can now perform custom authentication logic and respond with a success or failure status. If successful the connection is finally established.

Remember, your callback closure will only be called if the user opens the WIFI Settings page. Also, the user must manually initiate a connection to a hotspot. Your code can not automatically connect.

A sample code for the lambda can look like this.

{ (cmd: NEHotspotHelperCommand) in
    NSLog("Received command: \(cmd.commandType.rawValue)")
    if cmd.commandType == NEHotspotHelperCommandType.filterScanList {
        //Get all available hotspots
        var list: [NEHotspotNetwork] = cmd.networkList! 
        //Figure out the hotspot you wish to connect to
        let desiredNetwork : NEHotspotNetwork? = getBestScanResult(list) 

        if let network = desiredNetwork {
            network.setPassword("password") //Set the WIFI password

            let response = cmd.createResponse(NEHotspotHelperResult.success)

            response.deliver() //Respond back with the filtered list
    } else if cmd.commandType == NEHotspotHelperCommandType.evaluate {
        if let network = {
            //Set high confidence for the network

            let response = cmd.createResponse(NEHotspotHelperResult.success)
            response.deliver() //Respond back
    } else if cmd.commandType == NEHotspotHelperCommandType.authenticate {
        //Perform custom authentication and respond back with success
        // if all is OK
        let response = cmd.createResponse(NEHotspotHelperResult.success)
        response.deliver() //Respond back

For additional details about the different command types, see the official document.


33 thoughts on “Working with NEHotspotHelper

  1. Did you have to add the manually to the entitelement file?
    I received the networkextension entitelement from apple, add this property to the entitelement file, but somehow my registered helper never get called

    • Hi, I am not sure what an “entitlement file” is. From my experience when you are granted an entitlement, you need to add it to your development provisioning profile (using iOS developer web site). You need to then download the provisioning profile and install it in your developer machine.

  2. Sorry, I’m not sure if I understand: the NEHotspotHelper could make possible if you want to automatically join to a specific WLAN (like the wifi of your store).

      • But, if the user enter in the range of you hotspot, you can show any alert or something to invite for join? Like ‘Hey, do you want to access internet with our wifi?’

      • As I have said in my post: “Remember, your callback closure will only be called if the user opens the WIFI Settings page. Also, the user must manually initiate a connection to a hotspot. Your code can not automatically connect.”

        So, technically once the user opens the WIFI settings page and your callback gets called you can post a notification asking the user to join a hotspot.

  3. At the Authentication point,
    //Perform custom authentication and respond back with success
    // if all is OK

    I am unable to make any webrequests, do you perhaps know how to get around this?
    Only after I have delivered the result, there is a small window (300ms ~ 500ms) where I am able to do web calls but then the code pauses after that.

    • Deliver the result after you have made the web call and not before. The result should contain a success or failure flag as shown in the example code. Presumably the web call will tell you what that flag should be.

      • I understand. Try making a synchronous web call. So that the web call will block until the response comes back. Then deliver the result. As a proof of concept try replacing the web call with a blocking sleep call and wait for say 5 seconds.


        Your app should not get terminated while it is waiting. If this works then replace the sleep with a blocking web call.

      • Thank you Bibhas2. I have managed to get it working before delivering the response. Basically the gateway has a local domain name and an IP address (Default Gateway IP) – So I made a web request to the default ip instead of the domain name, and it works perfect now. After I authenticate the user on the gateway, I deliver the response.

      • I am currently trying to do the same thing.

        I’d like to a make a web request before responding with success in the ‘authenticating’ state.

        How were you able to get this working?

      • The problem is that I am unable to send a web request before the authentication state machine is in the ‘authenticated’ state. I need to make web requests when I get into the ‘authenticate’ state, i.e. when the command type is NEHotspotHelperCommandType.authenticate. When a response of success is sent from the ‘authenticate’ state, I can then send web requests. However, I need this to happen before.

        I’m connecting to the internet via a network with a captive portal, so it’s not as simple as providing a password to authenticate. I also posted a question on stack overflow with my code.

      • Hi, I think your problem is same as what Pierre reported here. I posted an answer in SO to your question. Here it is again.

        Others have reported problem with name server resolution from NEHotspotHelper callback. Try using an IP address to make the call.

        Also don’t forget URLSession works asynchronously. You will need to call “response.deliver()“ only after the web service calls finishes.

      • Thanks for your reply.

        I tried using an IP address and synchronous web call using semaphores. I still receive error – code: -1009, which means no internet connection according to iOS documents. I’m also new to Swift/iOS programming. I’ve updated my code in the Stack Overflow question. I look forward to your response and thanks for your help so far.

      • Try testing your web service call code from a regular application first. This will make sure you are not doing anything wrong and the server is accessible. If that works then you can try it from the hotspot helper.

      • I tried your suggestion and created another app, QueryURL, copied my queryUrl(){} function.
        The web request work when connected to a network with WiFi.
        It obviously fails when connected to a network without internet connectivity. (an unauthenticated captive portal hotspot)

        On the same iOS device, I have my incomplete HotspotHelper app that lets the OS go into the ‘Authenticated’ state even though the captive portal authentication was not successful (the current issue). When I use the QueryURL app to send a web request., the web request works. This is exactly the behaviour I was hoping for in the ‘Authenticating’ state, but does not work. What do you think?

  4. Hi,
    Do I need to create any specific iOS Extension to use NEHotspotHelper?. It would be great if you could guide me with the project creation process. I was going through the developer site, but there are hardly any references.

  5. Thanks for the great article! The Apple documentation is not so clear…
    I’m currently working on a project whose main feature is the handling of a public OPEN network (connect and disconnect, login and logout, …).
    Right now, the user, from the settings, joins the free network, the OS shows the captive portal and the user has to press cancel, choose “Use without Internet” and then open the app.
    If I register my app as Hotspot Helper, is it possible that the OS will open the app instead of the browser with a captive portal? Or have I to perform the auth call in background?

    • Hi, the NEHotspotHelper was designed to work without any GUI. The callbacks need to respond back pretty quickly and you can not have user interactivity for that.

      However, it is possible, after your callback has done the authentication to launch an app using the usual URL scheme. I never tried that but should work.

  6. I need some clarity:
    I need to open a list of all available near by Wifi within my app.
    If I use NEHotspotHelperCommandType.filterScanList then will it provide me resultant list OR it will only provide after I jump to the Setting -> Wifi in background.

    I have tried it but my closure is not getting called where I have checked NEHotspotHelperCommandType.filterScanList.

    I am unable to rectify it as I have added all the possible capabilities and entitlement in my app and provisioning profile.

    – (BOOL)scanWifiInfo {
    self.outputLabel.text = @”1.Start”;

    NSMutableDictionary* options = [[NSMutableDictionary alloc] init];
    [options setObject:@”myMoto Wifi” forKey: kNEHotspotHelperOptionDisplayName];
    dispatch_queue_t queue = dispatch_queue_create(“EFNEHotspotHelperDemo”, DISPATCH_QUEUE_CONCURRENT);

    self.outputLabel.text = @”2.Try”;

    __weak typeof(self) weakself = self;
    BOOL returnType = [NEHotspotHelper registerWithOptions: options queue: queue handler: ^(NEHotspotHelperCommand * cmd) {


    NSMutableString* resultString = [[NSMutableString alloc] initWithString: @””];

    NEHotspotNetwork* network;
    if (cmd.commandType == kNEHotspotHelperCommandTypeEvaluate || cmd.commandType == kNEHotspotHelperCommandTypeFilterScanList) {
    for (network in cmd.networkList) {
    NSString* wifiInfoString = [[NSString alloc] initWithFormat: @”SSID: %@\nMac Address: %@\n Command Type: %f\nCommandType:%ld\n\n”,
    network.SSID, network.BSSID, network.signalStrength, (long)cmd.commandType];
    NSLog(@”%@”, wifiInfoString);
    [resultString appendString: wifiInfoString];

    if ([network.SSID isEqualToString: @”myMoto WiFi”]) {
    [network setConfidence: kNEHotspotHelperConfidenceHigh];
    [network setPassword: @”123456789xxx”];
    NEHotspotHelperResponse *response = [cmd createResponse: kNEHotspotHelperResultSuccess];
    NSLog(@”Response CMD: %@”, response);
    [response setNetworkList: @[network]];
    [response setNetwork: network];
    [response deliver];

    weakself.infoString = resultString;

    NSString* logString = [[NSString alloc] initWithFormat: @”3.Result: %@”, returnType == YES ? @”Yes” : @”No”];
    NSLog(@”%@”, logString);
    self.outputLabel.text = logString;

    return returnType;

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.