iPhone get SSID without private library
Asked Answered
I

9

156

I have a commercial app that has a completely legitimate reason to see the SSID of the network it is connected to: If it is connected to a Adhoc network for a 3rd party hardware device it needs to be functioning in a different manner than if it is connected to the internet.

Everything I've seen about getting the SSID tells me I have to use Apple80211, which I understand is a private library. I also read that if I use a private library Apple will not approve the app.

Am I stuck between an Apple and a hard place, or is there something I'm missing here?

Idalla answered 4/3, 2011 at 20:9 Comment(1)
Related: #352454Religieux
I
187

As of iOS 7 or 8, you can do this (need Entitlement for iOS 12+ as shown below):

@import SystemConfiguration.CaptiveNetwork;

/** Returns first non-empty SSID network info dictionary.
 *  @see CNCopyCurrentNetworkInfo */
- (NSDictionary *)fetchSSIDInfo {
    NSArray *interfaceNames = CFBridgingRelease(CNCopySupportedInterfaces());
    NSLog(@"%s: Supported interfaces: %@", __func__, interfaceNames);

    NSDictionary *SSIDInfo;
    for (NSString *interfaceName in interfaceNames) {
        SSIDInfo = CFBridgingRelease(
            CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName));
        NSLog(@"%s: %@ => %@", __func__, interfaceName, SSIDInfo);

        BOOL isNotEmpty = (SSIDInfo.count > 0);
        if (isNotEmpty) {
            break;
        }
    }
    return SSIDInfo;
}

Example output:

2011-03-04 15:32:00.669 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: Supported interfaces: (
    en0
)
2011-03-04 15:32:00.693 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: en0 => {
    BSSID = "ca:fe:ca:fe:ca:fe";
    SSID = XXXX;
    SSIDDATA = <01234567 01234567 01234567>;
}

Note that no ifs are supported on the simulator. Test on your device.

iOS 12

You must enable access wifi info from capabilities.

Important To use this function in iOS 12 and later, enable the Access WiFi Information capability for your app in Xcode. When you enable this capability, Xcode automatically adds the Access WiFi Information entitlement to your entitlements file and App ID. Documentation link

Swift 4.2

func getConnectedWifiInfo() -> [AnyHashable: Any]? {

    if let ifs = CFBridgingRetain( CNCopySupportedInterfaces()) as? [String],
        let ifName = ifs.first as CFString?,
        let info = CFBridgingRetain( CNCopyCurrentNetworkInfo((ifName))) as? [AnyHashable: Any] {

        return info
    }
    return nil

}
Immunogenic answered 4/3, 2011 at 20:34 Comment(10)
Thanks! If you're using ARC, here's what it should look like: - (id)fetchSSIDInfo { NSArray *ifs = (bridge_transfer id)CNCopySupportedInterfaces(); NSLog(@"%s: Supported interfaces: %@", _func, ifs); id info = nil; for (NSString *ifnam in ifs) { info = (bridge_transfer id)CNCopyCurrentNetworkInfo((_bridge CFStringRef)ifnam); NSLog(@"%s: %@ => %@", __func, ifnam, info); if (info && [info count]) { break; } } return info; }Belshazzar
+1 Works great! Do not forget to add/link the [+]framework to your project. If you see weird compiling errors when using this method that is probably your problem. To e.g. get the SSID from the returned dictionary use // Getting a dictionary object containing the information of the network the iPhone is connected to NSDictionary *networkDict = [self fetchSSIDInfo]; // Select the SSID from the network information NSString *iPhoneNetworkSSID = [networkDict objectForKey:@"SSID"];Wallraff
does anyone know what BSSID is? it looks like MAC address of a router, but it's actually not. neither it is device's MAC address.Platt
Nice answer, but core foundation is handy so much here. CFArrayGetValueAtIndex and CFDictionaryGetValue are OK.Shingle
@Platt Roughly, SSID = network name, BSSID = AP MAC-like string (must be unique within an SSID). There can be multiple access points in a single network, so the BSSID identifies which you're talking to. See juniper.net/techpubs/en_US/junos-space-apps12.3/….Immunogenic
@Filip Updated ARC-friendly code address this by using modular includes (@import rather than #import). Clang will automatically link in the needed framework when a module, rather than just a header, is imported.Immunogenic
"Note that no ifs are supported on the simulator." - What do you mean? No IF's at all?? How would that be a useful simulator?Advertence
@ckonig: It simulates most of the UIKit bits. Doesn't do 3D touch, doesn't handle multitouch even with trackpads that support it, didn't support A/V for a few years. If it's a problem, you can file a radar, but grabbing an iOS device will be a far faster fix.Immunogenic
iOS 12 now requires the entitlement to be enabled in the capabilities developer.apple.com/documentation/systemconfiguration/…Ahead
iOS 13 beta now requires location permission in addition to the capability for CNCopyCurrentNetworkInfo to return useful information; otherwise it returns nil. See: #56584150Sindhi
A
62

UPDATE FOR iOS 10 and up

CNCopySupportedInterfaces is no longer deprecated in iOS 10. (API Reference)

You need to import SystemConfiguration/CaptiveNetwork.h and add SystemConfiguration.framework to your target's Linked Libraries (under build phases).

Here is a code snippet in swift (RikiRiocma's Answer):

import Foundation
import SystemConfiguration.CaptiveNetwork

public class SSID {
    class func fetchSSIDInfo() -> String {
        var currentSSID = ""
        if let interfaces = CNCopySupportedInterfaces() {
            for i in 0..<CFArrayGetCount(interfaces) {
                let interfaceName: UnsafePointer<Void> = CFArrayGetValueAtIndex(interfaces, i)
                let rec = unsafeBitCast(interfaceName, AnyObject.self)
                let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)")
                if unsafeInterfaceData != nil {
                    let interfaceData = unsafeInterfaceData! as Dictionary!
                    currentSSID = interfaceData["SSID"] as! String
                }
            }
        }
        return currentSSID
    }
}

(Important: CNCopySupportedInterfaces returns nil on simulator.)

For Objective-c, see Esad's answer here and below

+ (NSString *)GetCurrentWifiHotSpotName {    
    NSString *wifiName = nil;
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info[@"SSID"]) {
            wifiName = info[@"SSID"];
        }
    }
    return wifiName;
}

UPDATE FOR iOS 9

As of iOS 9 Captive Network is deprecated*. (source)

*No longer deprecated in iOS 10, see above.

It's recommended you use NEHotspotHelper (source)

You will need to email apple at [email protected] and request entitlements. (source)

Sample Code (Not my code. See Pablo A's answer):

for(NEHotspotNetwork *hotspotNetwork in [NEHotspotHelper supportedNetworkInterfaces]) {
    NSString *ssid = hotspotNetwork.SSID;
    NSString *bssid = hotspotNetwork.BSSID;
    BOOL secure = hotspotNetwork.secure;
    BOOL autoJoined = hotspotNetwork.autoJoined;
    double signalStrength = hotspotNetwork.signalStrength;
}

Side note: Yup, they deprecated CNCopySupportedInterfaces in iOS 9 and reversed their position in iOS 10. I spoke with an Apple networking engineer and the reversal came after so many people filed Radars and spoke out about the issue on the Apple Developer forums.

Appleby answered 14/10, 2015 at 18:6 Comment(1)
I got approved by apple to use network extension, but I still got empty array from [NEHotspotHelper supportedNetworkInterfaces]. Do you know the possible reason?Raisin
M
61

Here's the cleaned up ARC version, based on @elsurudo's code:

- (id)fetchSSIDInfo {
     NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces();
     NSLog(@"Supported interfaces: %@", ifs);
     NSDictionary *info;
     for (NSString *ifnam in ifs) {
         info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
         NSLog(@"%@ => %@", ifnam, info);
         if (info && [info count]) { break; }
     }
     return info;
}
Marilee answered 12/1, 2013 at 0:5 Comment(4)
@mindbomb is right, here is a question about that issue: #31556140Harness
yea, Apple re-enabled CaptiveNetwork API after deprecating in on the iOS9 betas..Diffract
@Diffract is this still the case? I am having issues implementing this.Hiero
@Hiero Note that this only works on device, on Simulator an empty is returnedMarilee
O
28

This works for me on the device (not simulator). Make sure you add the systemconfiguration framework.

#import <SystemConfiguration/CaptiveNetwork.h>

+ (NSString *)currentWifiSSID {
    // Does not work on the simulator.
    NSString *ssid = nil;
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info[@"SSID"]) {
            ssid = info[@"SSID"];
        }
    }
    return ssid;
}
Overboard answered 6/3, 2013 at 0:10 Comment(0)
S
10

This code work well in order to get SSID.

#import <SystemConfiguration/CaptiveNetwork.h>

@implementation IODAppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{


CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(@"Connected at:%@",myDict);
NSDictionary *myDictionary = (__bridge_transfer NSDictionary*)myDict;
NSString * BSSID = [myDictionary objectForKey:@"BSSID"];
NSLog(@"bssid is %@",BSSID);
// Override point for customization after application launch.
return YES;
}

And this is the results :

Connected at:{
BSSID = 0;
SSID = "Eqra'aOrange";
SSIDDATA = <45717261 27614f72 616e6765>;

}

Samurai answered 14/7, 2013 at 21:26 Comment(0)
K
9

If you are running iOS 12 you will need to do an extra step. I've been struggling to make this code work and finally found this on Apple's site: "Important To use this function in iOS 12 and later, enable the Access WiFi Information capability for your app in Xcode. When you enable this capability, Xcode automatically adds the Access WiFi Information entitlement to your entitlements file and App ID." https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo

Kanarese answered 8/7, 2018 at 17:6 Comment(0)
E
5

Here's the short & sweet Swift version.

Remember to link and import the Framework:

import UIKit
import SystemConfiguration.CaptiveNetwork

Define the method:

func fetchSSIDInfo() -> CFDictionary? {
    if let
        ifs = CNCopySupportedInterfaces().takeUnretainedValue() as? [String],
        ifName = ifs.first,
        info = CNCopyCurrentNetworkInfo((ifName as CFStringRef))
    {
        return info.takeUnretainedValue()
    }
    return nil
}

Call the method when you need it:

if let
    ssidInfo = fetchSSIDInfo() as? [String:AnyObject],
    ssID = ssidInfo["SSID"] as? String
{
    println("SSID: \(ssID)")
} else {
    println("SSID not found")
}

As mentioned elsewhere, this only works on your iDevice. When not on WiFi, the method will return nil – hence the optional.

Exempt answered 1/6, 2015 at 14:2 Comment(0)
M
5

For iOS 13

As from iOS 13 your app also needs Core Location access in order to use the CNCopyCurrentNetworkInfo function unless it configured the current network or has VPN configurations:
excerpt from https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

So this is what you need (see apple documentation):
- Link the CoreLocation.framework library
- Add location-services as a UIRequiredDeviceCapabilities Key/Value in Info.plist
- Add a NSLocationWhenInUseUsageDescription Key/Value in Info.plist describing why your app requires Core Location
- Add the "Access WiFi Information" entitlement for your app

Now as an Objective-C example, first check if location access has been accepted before reading the network info using CNCopyCurrentNetworkInfo:

- (void)fetchSSIDInfo {
    NSString *ssid = NSLocalizedString(@"not_found", nil);

    if (@available(iOS 13.0, *)) {
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
            NSLog(@"User has explicitly denied authorization for this application, or location services are disabled in Settings.");
        } else {
            CLLocationManager* cllocation = [[CLLocationManager alloc] init];
            if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
                [cllocation requestWhenInUseAuthorization];
                usleep(500);
                return [self fetchSSIDInfo];
            }
        }
    }

    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    id info = nil;
    for (NSString *ifnam in ifs) {
        info = (__bridge_transfer id)CNCopyCurrentNetworkInfo(
            (__bridge CFStringRef)ifnam);

        NSDictionary *infoDict = (NSDictionary *)info;
        for (NSString *key in infoDict.allKeys) {
            if ([key isEqualToString:@"SSID"]) {
                ssid = [infoDict objectForKey:key];
            }
        }
    }        
        ...
    ...
}
Mitchel answered 15/10, 2019 at 8:41 Comment(2)
That is required on iOS13, otherwise you got nil for CNCopyCurrentNetworkInfo()Hobbyhorse
@Sugar yes indeed iOS13 - see the first line of my answerMitchel

© 2022 - 2024 — McMap. All rights reserved.