Detect if Secure Enclave is available on current device - ios

Is there a certain way to detect if storing in Secure Enclave is available on current device?

Here is another solution:
Device.h
#import <Foundation/Foundation.h>
#interface Device : NSObject
+(BOOL) hasSecureEnclave;
+(BOOL) isSimulator;
+(BOOL) hasBiometrics;
#end
Device.m
#import "Device.h"
#import <LocalAuthentication/LocalAuthentication.h>
#implementation Device
//To check that device has secure enclave or not
+(BOOL) hasSecureEnclave {
NSLog(#"IS Simulator : %d", [Device isSimulator]);
return [Device hasBiometrics] && ![Device isSimulator] ;
}
//To Check that this is this simulator
+(BOOL) isSimulator {
return TARGET_OS_SIMULATOR == 1;
}
//Check that this device has Biometrics features available
+(BOOL) hasBiometrics {
//Local Authentication Context
LAContext *localAuthContext = [[LAContext alloc] init];
NSError *error = nil;
/// Policies can have certain requirements which, when not satisfied, would always cause
/// the policy evaluation to fail - e.g. a passcode set, a fingerprint
/// enrolled with Touch ID or a face set up with Face ID. This method allows easy checking
/// for such conditions.
BOOL isValidPolicy = [localAuthContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
if (isValidPolicy) {
if (#available(ios 11.0, *)){
if (error.code != kLAErrorBiometryNotAvailable){
isValidPolicy = true;
} else{
isValidPolicy = false;
}
}else{
if (error.code != kLAErrorTouchIDNotAvailable){
isValidPolicy = true;
}else{
isValidPolicy = false;
}
}
return isValidPolicy;
}
return isValidPolicy;
}
#end
If you want solution in Swift 4, then refer this link.
Solution in Swift 4

For a developer, there is exactly one thing the Secure Enclave can do: Create and hold private keys for elliptic curve cryptography, and encrypt or decrypt data using these keys. On iOS 9, the attributes describing elliptic curve algorithms are not there - therefore, if you are running iOS 9, then you can assume the Secure Enclave is not there, because you cannot use it.
On iOS 10 and above, there is just one way to decide guaranteed correctly if the Secure Enclave is present: Create an elliptic curve encryption key in the Secure Enclave, as described by Apple's documentation. If this fails, and the error has a code of -4 = errSecUnimplemented, then there is no Secure Enclave.
If you insist on checking a list of devices, you only need the devices that are documented as having no Secure Enclave but are able to run iOS 10, because on iOS 9 it is never available.

I did it myself:
+ (BOOL) isDeviceOkForSecureEnclave
{
double OSVersionNumber = floor(NSFoundationVersionNumber);
UIUserInterfaceIdiom deviceType = [[UIDevice currentDevice] userInterfaceIdiom];
BOOL isOSForSecureEnclave = OSVersionNumber > NSFoundationVersionNumber_iOS_8_4 ? YES:NO;
//iOS 9 and up are ready for SE
BOOL isDeviceModelForSecureEnclave = NO;
switch (deviceType) {
case UIUserInterfaceIdiomPhone:
//iPhone
isDeviceModelForSecureEnclave = [self isPhoneForSE];
break;
case UIUserInterfaceIdiomPad:
//iPad
isDeviceModelForSecureEnclave = [self isPadForSE];
break;
default:
isDeviceModelForSecureEnclave = false;
break;
}
return (isOSForSecureEnclave && isDeviceModelForSecureEnclave) ? YES:NO;
}
/**
The arrays are models that we know not having SE in hardware, so if the current device is on the list it means it dosent have SE
*/
+ (BOOL) isPhoneForSE
{
NSString *thisPlatform = [self platform];
NSArray * oldModels = [NSArray arrayWithObjects:
#"x86_64",
#"iPhone1,1",
#"iPhone1,2",
#"iPhone2,1",
#"iPhone3,1",
#"iPhone3,3",
#"iPhone4,1",
#"iPhone5,1",
#"iPhone5,2",
#"iPhone5,3",
#"iPhone5,4", nil];
BOOL isInList = [oldModels containsObject: thisPlatform];
return !isInList;
}
+ (BOOL) isPadForSE
{
//iPad Mini 2 is the earliest with SE // "iPad4,4"
NSString *thisPlatform = [self platform];
NSArray * oldModels = [NSArray arrayWithObjects:
#"x86_64",
#"#iPad",
#"#iPad1,0",
#"#iPad1,1",
#"iPad2,1",
#"iPad2,2",
#"iPad2,3",
#"iPad2,4",
#"iPad2,5",
#"iPad2,6",
#"iPad2,7",
#"iPad3,1",
#"iPad3,2",
#"iPad3,3",
#"iPad3,4",
#"iPad3,5",
#"iPad3,6",nil];
BOOL isInList = [oldModels containsObject: thisPlatform];
return !isInList;
}
+ (NSString *)platform
{
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine = malloc(size);
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithUTF8String:machine];
free(machine);
return platform;
}
#end

There is a more straightforward way to check if Secure Enclave is available using the CryptoKit framework. The following approach is iOS 13+ and Swift only.
import CryptoKit
if TARGET_OS_SIMULATOR == 0 && SecureEnclave.isAvailable {
// use Secure Enclave
}
Additional check for Simulator is needed since SecureEnclave.isAvailable returns true running on a Simulator (checked on iOS 14.4).

Related

How to check either secure enclave is available in device or not

As we know that secure Enclave is a coprocessor fabricated in the Apple A7 and its available in A7 and later on but its use publicly in iOS 9 kSecAttrTokenIDSecureEnclave but how do we check either some device support secure enclave or not ?
Thanks
I didnt find any so I made my own check:
+ (BOOL) isDeviceOkForSecureEnclave
{
double OSVersionNumber = floor(NSFoundationVersionNumber);
UIUserInterfaceIdiom deviceType = [[UIDevice currentDevice] userInterfaceIdiom];
BOOL isOSForSecureEnclave = OSVersionNumber > NSFoundationVersionNumber_iOS_8_4 ? YES:NO;
//iOS 9 and up are ready for SE
BOOL isDeviceModelForSecureEnclave = NO;
switch (deviceType) {
case UIUserInterfaceIdiomPhone:
//iPhone
isDeviceModelForSecureEnclave = [self isPhoneForSE];
break;
case UIUserInterfaceIdiomPad:
//iPad
isDeviceModelForSecureEnclave = [self isPadForSE];
break;
default:
isDeviceModelForSecureEnclave = false;
break;
}
return (isOSForSecureEnclave && isDeviceModelForSecureEnclave) ? YES:NO;
}
/**
The arrays are models that we know not having SE in hardware, so if the current device is on the list it means it dosent have SE
*/
+ (BOOL) isPhoneForSE
{
NSString *thisPlatform = [self platform];
NSArray * oldModels = [NSArray arrayWithObjects:
#"x86_64",
#"iPhone1,1",
#"iPhone1,2",
#"iPhone2,1",
#"iPhone3,1",
#"iPhone3,3",
#"iPhone4,1",
#"iPhone5,1",
#"iPhone5,2",
#"iPhone5,3",
#"iPhone5,4", nil];
BOOL isInList = [oldModels containsObject: thisPlatform];
return !isInList;
}
+ (BOOL) isPadForSE
{
//iPad Mini 2 is the earliest with SE // "iPad4,4"
NSString *thisPlatform = [self platform];
NSArray * oldModels = [NSArray arrayWithObjects:
#"x86_64",
#"#iPad",
#"#iPad1,0",
#"#iPad1,1",
#"iPad2,1",
#"iPad2,2",
#"iPad2,3",
#"iPad2,4",
#"iPad2,5",
#"iPad2,6",
#"iPad2,7",
#"iPad3,1",
#"iPad3,2",
#"iPad3,3",
#"iPad3,4",
#"iPad3,5",
#"iPad3,6",nil];
BOOL isInList = [oldModels containsObject: thisPlatform];
return !isInList;
}
+ (NSString *)platform
{
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine = malloc(size);
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithUTF8String:machine];
free(machine);
return platform;
}
#end
TO Check Touch ID
- (BOOL)canAuthenticateByTouchId {
if ([LAContext class]) {
return [[[LAContext alloc] init] canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil];
}
return YES;
}
You can also find for detecting Secure Enclave here you find
Above solution has no problem but it seems like hack, so I am adding another solution in Swift 4.
To check Secure Enclave availability
enum Device {
//To check that device has secure enclave or not
public static var hasSecureEnclave: Bool {
return !isSimulator && hasBiometrics
}
//To Check that this is this simulator
public static var isSimulator: Bool {
return TARGET_OS_SIMULATOR == 1
}
//Check that this device has Biometrics features available
private static var hasBiometrics: Bool {
//Local Authentication Context
let localAuthContext = LAContext()
var error: NSError?
/// Policies can have certain requirements which, when not satisfied, would always cause
/// the policy evaluation to fail - e.g. a passcode set, a fingerprint
/// enrolled with Touch ID or a face set up with Face ID. This method allows easy checking
/// for such conditions.
var isValidPolicy = localAuthContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
guard isValidPolicy == true else {
if #available(iOS 11, *) {
if error!.code != LAError.biometryNotAvailable.rawValue {
isValidPolicy = true
} else{
isValidPolicy = false
}
}
else {
if error!.code != LAError.touchIDNotAvailable.rawValue {
isValidPolicy = true
}else{
isValidPolicy = false
}
}
return isValidPolicy
}
return isValidPolicy
}
}
To check that touch id available or not
let hasTouchID = LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
if(hasTouchID || (error?.code != LAError.touchIDNotAvailable.rawValue)) {
print("Touch Id Available in device")
}
If you want solution in Objective C, then refer this link.
Solution in Objective C.

How to detect in iOS wether a user has secured device? [duplicate]

I know the Touch ID on the iPhone 5S cannot be consumed by any other apps through the SDK, but I wanted to see if it was possible for an app to at least see if the Touch ID is enabled on the device. This can act as an additional security factor for an app to see if the Touch ID has been enabled on an iPhone 5S. I know MDM products can do this, is there a special API an app would need to use to determine this information?
The method you are looking for is LAContext's canEvaluatePolicy:error:.
It will return NO in the following cases :
Touch ID is not available on the device.
A passcode is not set on the device.
Touch ID has no enrolled fingers.
More infos here : Local Authentication Framework Reference
- (BOOL) isTouchIDAvailable {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
NSLog(#"%#", [authError localizedDescription]);
return NO;
}
return YES;
}
Since TouchID is only appeared in iPhone 5s, you can use the following code to determine hardware model:
- (NSString *) checkiPhone5s {
// Gets a string with the device model
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine = malloc(size);
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding];
free(machine);
if ([platform isEqualToString:#"iPhone6,1"]) {
// it is iPhone 5s !!!
}
}
Note that iPhone 5s may have new model, so that the platform string may be "iPhone6,2" , "iPhone6,3" in the future.
Alternatively, UIDeviceHardware class may provide useful information if that class is updated for iPhone 5s. Currently it shows "Unknown iPhone" for iPhone 5.
I check this by below stages.
iOS < 8.0:
self.touchIdAvailable = NO;
Then check if device is touchId available.
- (BOOL)canEvaluatePolicy:(LAPolicy)policy error:(NSError **)error
if ("canEvaluatePolicy return YES"):{
self.touchIdAvailable = YES;
self.osTouchIdStatus = TouchIdSetOK;
}
check why this device is not touchID enable in error block
case LAErrorPasscodeNotSet:
case LAErrorTouchIDNotEnrolled:
self.touchIdAvailable = YES;
self.osTouchIdStatus = TouchIdNotSet;
break;
default:
self.touchIdAvailable = NO;
self.osTouchIdStatus = TouchIdNotSupported;
break;
There doesn't appear to be anything mentioned in the developer documentation about detecting if Touch ID is present on the device. There most definitely is a private API that can give you that information but using it would be grounds for Apple not approving your app.

See if Touch ID is enabled on iPhone 5s

I know the Touch ID on the iPhone 5S cannot be consumed by any other apps through the SDK, but I wanted to see if it was possible for an app to at least see if the Touch ID is enabled on the device. This can act as an additional security factor for an app to see if the Touch ID has been enabled on an iPhone 5S. I know MDM products can do this, is there a special API an app would need to use to determine this information?
The method you are looking for is LAContext's canEvaluatePolicy:error:.
It will return NO in the following cases :
Touch ID is not available on the device.
A passcode is not set on the device.
Touch ID has no enrolled fingers.
More infos here : Local Authentication Framework Reference
- (BOOL) isTouchIDAvailable {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
NSLog(#"%#", [authError localizedDescription]);
return NO;
}
return YES;
}
Since TouchID is only appeared in iPhone 5s, you can use the following code to determine hardware model:
- (NSString *) checkiPhone5s {
// Gets a string with the device model
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine = malloc(size);
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding];
free(machine);
if ([platform isEqualToString:#"iPhone6,1"]) {
// it is iPhone 5s !!!
}
}
Note that iPhone 5s may have new model, so that the platform string may be "iPhone6,2" , "iPhone6,3" in the future.
Alternatively, UIDeviceHardware class may provide useful information if that class is updated for iPhone 5s. Currently it shows "Unknown iPhone" for iPhone 5.
I check this by below stages.
iOS < 8.0:
self.touchIdAvailable = NO;
Then check if device is touchId available.
- (BOOL)canEvaluatePolicy:(LAPolicy)policy error:(NSError **)error
if ("canEvaluatePolicy return YES"):{
self.touchIdAvailable = YES;
self.osTouchIdStatus = TouchIdSetOK;
}
check why this device is not touchID enable in error block
case LAErrorPasscodeNotSet:
case LAErrorTouchIDNotEnrolled:
self.touchIdAvailable = YES;
self.osTouchIdStatus = TouchIdNotSet;
break;
default:
self.touchIdAvailable = NO;
self.osTouchIdStatus = TouchIdNotSupported;
break;
There doesn't appear to be anything mentioned in the developer documentation about detecting if Touch ID is present on the device. There most definitely is a private API that can give you that information but using it would be grounds for Apple not approving your app.

UIDevice uniqueIdentifier deprecated - What to do now?

It has just come to light that the UIDevice uniqueIdentifier property is deprecated in iOS 5 and unavailable in iOS 7 and above. No alternative method or property appears to be available or forthcoming.
Many of our existing apps are tightly dependent on this property for uniquely identifying a particular device. How might we handle this problem going forward?
The suggestion from the documentation in 2011-2012 was:
Special Considerations
Do not use the uniqueIdentifier property. To create a unique identifier specific
to your app, you can call the CFUUIDCreate function to create a UUID, and write
it to the defaults database using the NSUserDefaults class.
However this value won't be the same if a user uninstalls and re-installs the app.
A UUID created by CFUUIDCreate is unique if a user uninstalls and re-installs the app: you will get a new one each time.
But you might want it to be not unique, i. e. it should stay the same when the user uninstalls and re-installs the app. This requires a bit of effort, since the most reliable per-device-identifier seems to be the MAC address. You could query the MAC and use that as UUID.
Edit: One needs to always query the MAC of the same interface, of course. I guess the best bet is with en0. The MAC is always present, even if the interface has no IP/is down.
Edit 2: As was pointed out by others, the preferred solution since iOS 6 is -[UIDevice identifierForVendor]. In most cases, you should be able use it as a drop-in replacement to the old -[UIDevice uniqueIdentifier] (but a UUID that is created when the app starts for the first time is what Apple seems to want you to use).
Edit 3: So this major point doesn't get lost in the comment noise: do not use the MAC as UUID, create a hash using the MAC. That hash will always create the same result every time, even across reinstalls and apps (if the hashing is done in the same way). Anyways, nowadays (2013) this isn't necessary any more except if you need a "stable" device identifier on iOS < 6.0.
Edit 4: In iOS 7, Apple now always returns a fixed value when querying the MAC to specifically thwart the MAC as base for an ID scheme. So you now really should use -[UIDevice identifierForVendor] or create a per-install UUID.
You can use your alternative for Apple UDID already. Kind guy gekitz wrote category on UIDevice which will generate some kind of UDID based on device mac-address and bundle identifier.
You can find code on github
Based on the link proposed by #moonlight, i did several tests and it seems to be the best solution. As #DarkDust says the method goes to check en0 which is always available.
There are 2 options:
uniqueDeviceIdentifier (MD5 of MAC+CFBundleIdentifier)
and uniqueGlobalDeviceIdentifier(MD5 of the MAC), these always returns the same values.
Below the tests i've done (with the real device):
#import "UIDevice+IdentifierAddition.h"
NSLog(#"%#",[[UIDevice currentDevice] uniqueDeviceIdentifier]);
NSLog(#"%#",[[UIDevice currentDevice] uniqueGlobalDeviceIdentifier]);
XXXX21f1f19edff198e2a2356bf4XXXX - (WIFI)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (WIFI)GlobalAppUDID
XXXX21f1f19edff198e2a2356bf4XXXX - (3G)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (3G)GlobalAppUDID
XXXX21f1f19edff198e2a2356bf4XXXX - (GPRS)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (GPRS)GlobalAppUDID
XXXX21f1f19edff198e2a2356bf4XXXX - (AirPlane mode)UDID
XXXX7dc3c577446a2bcbd77935bdXXXX - (AirPlane mode)GlobalAppUDID
XXXX21f1f19edff198e2a2356bf4XXXX - (Wi-Fi)after removing and
reinstalling the app XXXX7dc3c577446a2bcbd77935bdXXXX (Wi-Fi) after
removing and installing the app
Hope it's useful.
EDIT:
As others pointed out, this solution in iOS 7 is no longer useful since uniqueIdentifier is no longer available and querying for MAC address now returns always 02:00:00:00:00:00
check this out,
we can use Keychain instead of NSUserDefaults class, to store UUID created by CFUUIDCreate.
with this way we could avoid for UUID recreation with reinstallation,
and obtain always same UUID for same application even user uninstall and reinstall again.
UUID will recreated just when device reset by user.
I tried this method with SFHFKeychainUtils and it's works like a charm.
Create your own UUID and then store it in the Keychain. Thus it persists even when your app gets uninstalled. In many cases it also persists even if the user migrates between devices (e.g. full backup and restore to another device).
Effectively it becomes a unique user identifier as far as you're concerned. (even better than device identifier).
Example:
I am defining a custom method for creating a UUID as :
- (NSString *)createNewUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString *)string autorelease];
}
You can then store it in KEYCHAIN on the very first launch of your app. So that after first launch, we can simply use it from keychain, no need to regenerate it. The main reason for using Keychain to store is: When you set the UUID to the Keychain, it will persist even if the user completely uninstalls the App and then installs it again. . So, this is the permanent way of storing it, which means the key will be unique all the way.
#import "SSKeychain.h"
#import <Security/Security.h>
On applictaion launch include the following code :
// getting the unique key (if present ) from keychain , assuming "your app identifier" as a key
NSString *retrieveuuid = [SSKeychain passwordForService:#"your app identifier" account:#"user"];
if (retrieveuuid == nil) { // if this is the first time app lunching , create key for device
NSString *uuid = [self createNewUUID];
// save newly created key to Keychain
[SSKeychain setPassword:uuid forService:#"your app identifier" account:#"user"];
// this is the one time process
}
Download SSKeychain.m and .h file from sskeychain and Drag SSKeychain.m and .h file to your project and add "Security.framework" to your project.
To use UUID afterwards simply use :
NSString *retrieveuuid = [SSKeychain passwordForService:#"your app identifier" account:#"user"];
Perhaps you can use:
[UIDevice currentDevice].identifierForVendor.UUIDString
Apple's documentation describes identifierForVender as follows:
The value of this property is the same for apps that come from the same vendor running on the same device. A different value is returned for apps on the same device that come from different vendors, and for apps on different devices regardless of vendor.
You may want to consider using OpenUDID which is a drop-in replacement for the deprecated UDID.
Basically, to match the UDID, the following features are required:
unique or sufficiently unique (a low probability collision is
probably very acceptable)
persistence across reboots, restores, uninstalls
available across apps of different vendors (useful to acquire users via CPI networks) -
OpenUDID fulfills the above and even has a built-in Opt-Out mechanism for later consideration.
Check http://OpenUDID.org it points to the corresponding GitHub.
Hope this helps!
As a side note, I would shy away from any MAC address alternative. While the MAC address appears like a tempting and universal solution, be sure that this low hanging fruit is poisoned. The MAC address is very sensitive, and Apple may very well deprecate access to this one before you can even say "SUBMIT THIS APP"... the MAC network address is used to authenticate certain devices on private lans (WLANs) or other virtual private networks (VPNs). .. it's even more sensitive than the former UDID!
I'm sure Apple have annoyed many people with this change. I develop a bookkeeping app for iOS and have an online service to sync changes made on different devices. The service maintains a database of all devices and the changes that need to be propagated to them. Therefore it's important to know which devices are which. I'm keeping track of devices using the UIDevice uniqueIdentifier and for what it's worth, here are my thoughts.
Generate a UUID and store in user defaults?
No good because this does not persist when the user deletes the app. If they install again later the online service should not create a new device record, that would waste resources on the server and give a list of devices containing the same one two or more times. Users would see more than one "Bob's iPhone" listed if they re-installed the app.
Generate a UUID and store in the keychain?
This was my plan, since it persists even when the app is uninstalled. But when restoring an iTunes backup to a new iOS device, the keychain is transferred if the backup is encrypted. This could lead to two devices containing the same device id if the old and new devices are both in service. These should be listed as two devices in the online service, even if the device name is the same.
Generate a hash the MAC address and bundle id?
This looks like the best solution for what I need. By hashing with the bundle id, the generated device id is not going to enable the device to be tracked across apps and I get a unique ID for the app+device combination.
It's interesting to note that Apple's own documentation refers to validating Mac App Store receipts by computing a hash of the system MAC address plus the bundle id and version. So this seems allowable by policy, whether it passes through app review I don't yet know.
It looks like for iOS 6, Apple is recommending you use the NSUUID class.
From the message now in the UIDevice docs for uniqueIdentifier property:
Deprecated in iOS 5.0. Use the identifierForVendor property of this
class or the advertisingIdentifier property of the ASIdentifierManager
class instead, as appropriate, or use the UUID method of the NSUUID
class to create a UUID and write it to the user defaults database.
May help:
use below code it will always Unique except you erase(Format) your device.
Objective-C:
Option 1: This will change on every install
UIDevice *uuid = [NSUUID UUID].UUIDString;
Option 2: This will be unique per vendor/Developer Apple Account
UIDevice *myDevice = [UIDevice currentDevice];
NSString *uuid = [[myDevice identifierForVendor] UUIDString];
Swift 5.X:
Option 1: This will change on every install
let uuid = UUID().uuidString
Option 2: This will be unique per vendor/Developer Apple Account
let myDevice = UIDevice.current
let uuid = myDevice.identifierForVendor?.uuidString
I would also suggest changing over from uniqueIdentifier to this open source library (2 simple categories really) that utilize the device’s MAC Address along with the App Bundle Identifier to generate a unique ID in your applications that can be used as a UDID replacement.
Keep in mind that unlike the UDID this number will be different for every app.
You simply need to import the included NSString and UIDevice categories and call [[UIDevice currentDevice] uniqueDeviceIdentifier] like so:
#import "UIDevice+IdentifierAddition.h"
#import "NSString+MD5Addition.h"
NSString *iosFiveUDID = [[UIDevice currentDevice] uniqueDeviceIdentifier]
You can find it on Github here:
UIDevice with UniqueIdentifier for iOS 5
Here are the categories (just the .m files - check the github project for the headers):
UIDevice+IdentifierAddition.m
#import "UIDevice+IdentifierAddition.h"
#import "NSString+MD5Addition.h"
#include <sys/socket.h> // Per msqr
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#interface UIDevice(Private)
- (NSString *) macaddress;
#end
#implementation UIDevice (IdentifierAddition)
////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Private Methods
// Return the local MAC addy
// Courtesy of FreeBSD hackers email list
// Accidentally munged during previous update. Fixed thanks to erica sadun & mlamb.
- (NSString *) macaddress{
    
    int mib[6];
    size_t len;
    char *buf;
    unsigned char *ptr;
    struct if_msghdr *ifm;
    struct sockaddr_dl *sdl;
    
    mib[0] = CTL_NET;
    mib[1] = AF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_LINK;
    mib[4] = NET_RT_IFLIST;
    
    if ((mib[5] = if_nametoindex("en0")) == 0) {
        printf("Error: if_nametoindex error\n");
        return NULL;
    }
    
    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 1\n");
        return NULL;
    }
    
    if ((buf = malloc(len)) == NULL) {
        printf("Could not allocate memory. error!\n");
        return NULL;
    }
    
    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 2");
        return NULL;
    }
    
    ifm = (struct if_msghdr *)buf;
    sdl = (struct sockaddr_dl *)(ifm + 1);
    ptr = (unsigned char *)LLADDR(sdl);
    NSString *outstring = [NSString stringWithFormat:#"%02X:%02X:%02X:%02X:%02X:%02X",
                           *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    free(buf);
    
    return outstring;
}
////////////////////////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark Public Methods
- (NSString *) uniqueDeviceIdentifier{
    NSString *macaddress = [[UIDevice currentDevice] macaddress];
    NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];  
    NSString *stringToHash = [NSString stringWithFormat:#"%#%#",macaddress,bundleIdentifier];
    NSString *uniqueIdentifier = [stringToHash stringFromMD5];  
    return uniqueIdentifier;
}
- (NSString *) uniqueGlobalDeviceIdentifier{
    NSString *macaddress = [[UIDevice currentDevice] macaddress];
    NSString *uniqueIdentifier = [macaddress stringFromMD5];    
    return uniqueIdentifier;
}
#end
NSString+MD5Addition.m:
#import "NSString+MD5Addition.h"
#import <CommonCrypto/CommonDigest.h>
#implementation NSString(MD5Addition)
- (NSString *) stringFromMD5{
    
    if(self == nil || [self length] == 0)
        return nil;
    
    const char *value = [self UTF8String];
    
    unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
    CC_MD5(value, strlen(value), outputBuffer);
    
    NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++){
        [outputString appendFormat:#"%02x",outputBuffer[count]];
    }
    return [outputString autorelease];
}
#end
You can achieve from this code : UIDevice-with-UniqueIdentifier-for-iOS-5
The MAC address can be spoofed which makes such an approach useless for tying content to specific users or implementing security features like blacklists.
After some further research it appears to me that we're left without a proper alternative as of now. I seriously hope Apple will reconsider their decision.
Maybe it would be a good idea to email Apple about this topic and / or file a bug / feature request on this since maybe they are not even aware of the full consequences for developers.
UIDevice identifierForVendor introduced in iOS 6 would work for your purposes.
identifierForVendor is an alphanumeric string that uniquely identifies a device to the app’s vendor. (read-only)
#property(nonatomic, readonly, retain) NSUUID *identifierForVendor
The value of this property is the same for apps that come from the same vendor running on the same device. A different value is returned for apps onthe same device that come from different vendors, and for apps on different devices regardles of vendor.
Available in iOS 6.0 and later and declared in UIDevice.h
For iOS 5 refer this link UIDevice-with-UniqueIdentifier-for-iOS-5
Using the SSKeychain and code mentioned above. Here's code to copy/paste (add SSKeychain module):
+(NSString *) getUUID {
//Use the bundle name as the App identifier. No need to get the localized version.
NSString *Appname = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleName"];
//Check if we have UUID already
NSString *retrieveuuid = [SSKeychain passwordForService:Appname account:#"user"];
if (retrieveuuid == NULL)
{
//Create new key for this app/device
CFUUIDRef newUniqueId = CFUUIDCreate(kCFAllocatorDefault);
retrieveuuid = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, newUniqueId);
CFRelease(newUniqueId);
//Save key to Keychain
[SSKeychain setPassword:retrieveuuid forService:Appname account:#"user"];
}
return retrieveuuid;
}
Following code helps to get UDID:
udid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
NSLog(#"UDID : %#", udid);
This is code I'm using to get ID for both iOS 5 and iOS 6, 7:
- (NSString *) advertisingIdentifier
{
if (!NSClassFromString(#"ASIdentifierManager")) {
SEL selector = NSSelectorFromString(#"uniqueIdentifier");
if ([[UIDevice currentDevice] respondsToSelector:selector]) {
return [[UIDevice currentDevice] performSelector:selector];
}
}
return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}
From iOS 6 onwards, we have NSUUID class which complies RFC4122
Apple Link : apple_ref for NSUUID
iOS 11 has introduced the DeviceCheck framework. It has a fullproof solution for uniquely identifying the device.
A working way to get UDID:
Launch a web server inside the app with two pages: one should return specially crafted MobileConfiguration profile and another should collect UDID. More info here, here and here.
You open the first page in Mobile Safari from inside the app and it redirects you to Settings.app asking to install configuration profile. After you install the profile, UDID is sent to the second web page and you can access it from inside the app. (Settings.app has all necessary entitlements and different sandbox rules).
An example using RoutingHTTPServer:
import UIKit
import RoutingHTTPServer
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var bgTask = UIBackgroundTaskInvalid
let server = HTTPServer()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
application.openURL(NSURL(string: "http://localhost:55555")!)
return true
}
func applicationDidEnterBackground(application: UIApplication) {
bgTask = application.beginBackgroundTaskWithExpirationHandler() {
dispatch_async(dispatch_get_main_queue()) {[unowned self] in
application.endBackgroundTask(self.bgTask)
self.bgTask = UIBackgroundTaskInvalid
}
}
}
}
class HTTPServer: RoutingHTTPServer {
override init() {
super.init()
setPort(55555)
handleMethod("GET", withPath: "/") {
$1.setHeader("Content-Type", value: "application/x-apple-aspen-config")
$1.respondWithData(NSData(contentsOfFile: NSBundle.mainBundle().pathForResource("udid", ofType: "mobileconfig")!)!)
}
handleMethod("POST", withPath: "/") {
let raw = NSString(data:$0.body(), encoding:NSISOLatin1StringEncoding) as! String
let plistString = raw.substringWithRange(Range(start: raw.rangeOfString("<?xml")!.startIndex,end: raw.rangeOfString("</plist>")!.endIndex))
let plist = NSPropertyListSerialization.propertyListWithData(plistString.dataUsingEncoding(NSISOLatin1StringEncoding)!, options: .allZeros, format: nil, error: nil) as! [String:String]
let udid = plist["UDID"]!
println(udid) // Here is your UDID!
$1.statusCode = 200
$1.respondWithString("see https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/ConfigurationProfileExamples/ConfigurationProfileExamples.html")
}
start(nil)
}
}
Here are the contents of udid.mobileconfig:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<dict>
<key>URL</key>
<string>http://localhost:55555</string>
<key>DeviceAttributes</key>
<array>
<string>IMEI</string>
<string>UDID</string>
<string>PRODUCT</string>
<string>VERSION</string>
<string>SERIAL</string>
</array>
</dict>
<key>PayloadOrganization</key>
<string>udid</string>
<key>PayloadDisplayName</key>
<string>Get Your UDID</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadUUID</key>
<string>9CF421B3-9853-9999-BC8A-982CBD3C907C</string>
<key>PayloadIdentifier</key>
<string>udid</string>
<key>PayloadDescription</key>
<string>Install this temporary profile to find and display your current device's UDID. It is automatically removed from device right after you get your UDID.</string>
<key>PayloadType</key>
<string>Profile Service</string>
</dict>
</plist>
The profile installation will fail (I didn't bother to implement an expected response, see documentation), but the app will get a correct UDID. And you should also sign the mobileconfig.
For Swift 3.0 please use below code.
let deviceIdentifier: String = (UIDevice.current.identifierForVendor?.uuidString)!
NSLog("output is : %#", deviceIdentifier)
You can use
NSString *sID = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
Which is unique for the device in all application.
Apple has added a new framework in iOS 11 called DeviceCheck which will help you to get the unique identifier very easily.
Read this form more information.
https://medium.com/#santoshbotre01/unique-identifier-for-the-ios-devices-590bb778290d
If someone stumble upon to this question, when searching for an alternative. I have followed this approach in IDManager class,
This is a collection from different solutions. KeyChainUtil is a wrapper to read from keychain.
You can also use the hashed MAC address as a kind of unique ID.
/* Apple confirmed this bug in their system in response to a Technical Support Incident 
    request. They said that identifierForVendor and advertisingIdentifier sometimes 
    returning all zeros can be seen both in development builds and apps downloaded over the 
    air from the App Store. They have no work around and can't say when the problem will be fixed. */
#define kBuggyASIID #"00000000-0000-0000-0000-000000000000"
+ (NSString *) getUniqueID {
if (NSClassFromString(#"ASIdentifierManager")) {
NSString * asiID = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
if ([asiID compare:kBuggyASIID] == NSOrderedSame) {
NSLog(#"Error: This device return buggy advertisingIdentifier.");
return [IDManager getUniqueUUID];
} else {
return asiID;
}
} else {
return [IDManager getUniqueUUID];
}
}
+ (NSString *) getUniqueUUID {
NSError * error;
NSString * uuid = [KeychainUtils getPasswordForUsername:kBuyassUser andServiceName:kIdOgBetilngService error:&error];
if (error) {
NSLog(#"Error geting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
if (!uuid) {
DLog(#"No UUID found. Creating a new one.");
uuid = [IDManager GetUUID];
uuid = [Util md5String:uuid];
[KeychainUtils storeUsername:USER_NAME andPassword:uuid forServiceName:SERVICE_NAME updateExisting:YES error:&error];
if (error) {
NSLog(#"Error getting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
}
return uuid;
}
/* NSUUID is after iOS 6. */
+ (NSString *)GetUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString *)string autorelease];
}
#pragma mark - MAC address
// Return the local MAC addy
// Courtesy of FreeBSD hackers email list
// Last fallback for unique identifier
+ (NSString *) getMACAddress
{
int mib[6];
size_t len;
char *buf;
unsigned char *ptr;
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = AF_LINK;
mib[4] = NET_RT_IFLIST;
if ((mib[5] = if_nametoindex("en0")) == 0) {
printf("Error: if_nametoindex error\n");
return NULL;
}
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
printf("Error: sysctl, take 1\n");
return NULL;
}
if ((buf = malloc(len)) == NULL) {
printf("Error: Memory allocation error\n");
return NULL;
}
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
printf("Error: sysctl, take 2\n");
free(buf); // Thanks, Remy "Psy" Demerest
return NULL;
}
ifm = (struct if_msghdr *)buf;
sdl = (struct sockaddr_dl *)(ifm + 1);
ptr = (unsigned char *)LLADDR(sdl);
NSString *outstring = [NSString stringWithFormat:#"%02X:%02X:%02X:%02X:%02X:%02X", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
free(buf);
return outstring;
}
+ (NSString *) getHashedMACAddress
{
NSString * mac = [IDManager getMACAddress];
return [Util md5String:mac];
}
+ (NSString *)md5String:(NSString *)plainText
{
if(plainText == nil || [plainText length] == 0)
return nil;
const char *value = [plainText UTF8String];
unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(value, strlen(value), outputBuffer);
NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++){
[outputString appendFormat:#"%02x",outputBuffer[count]];
}
NSString * retString = [NSString stringWithString:outputString];
[outputString release];
return retString;
}
+ (NSString *) getUniqueUUID {
NSError * error;
NSString * uuid = [KeychainUtils getPasswordForUsername:kBuyassUser andServiceName:kIdOgBetilngService error:&error];
if (error) {
NSLog(#"Error geting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
if (!uuid) {
DLog(#"No UUID found. Creating a new one.");
uuid = [IDManager GetUUID];
uuid = [Util md5String:uuid];
[KeychainUtils storeUsername:USER_NAME andPassword:uuid forServiceName:SERVICE_NAME updateExisting:YES error:&error];
if (error) {
NSLog(#"Error getting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
}
return uuid;
}
We can use identifierForVendor for ios7,
-(NSString*)uniqueIDForDevice
{
NSString* uniqueIdentifier = nil;
if( [UIDevice instancesRespondToSelector:#selector(identifierForVendor)] ) { // >=iOS 7
uniqueIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
} else { //<=iOS6, Use UDID of Device
CFUUIDRef uuid = CFUUIDCreate(NULL);
//uniqueIdentifier = ( NSString*)CFUUIDCreateString(NULL, uuid);- for non- ARC
uniqueIdentifier = ( NSString*)CFBridgingRelease(CFUUIDCreateString(NULL, uuid));// for ARC
CFRelease(uuid);
}
}
return uniqueIdentifier;
}
--Important Note ---
UDID and identifierForVendor are different:---
1.) On uninstalling and reinstalling the app identifierForVendor will change.
2.) The value of identifierForVendor remains the same for all the apps installed from the same vendor on the device.
3.) The value of identifierForVendor also changes for all the apps if any of the app (from same vendor) is reinstalled.
Apple has hidden the UDID from all public APIs, starting with iOS 7. Any UDID that begins with FFFF is a fake ID. The "Send UDID" apps that previously worked can no longer be used to gather UDID for test devices. (sigh!)
The UDID is shown when a device is connected to XCode (in the organizer), and when the device is connected to iTunes (although you have to click on 'Serial Number' to get the Identifier to display.
If you need to get the UDID for a device to add to a provisioning profile, and can't do it yourself in XCode, you will have to walk them through the steps to copy/paste it from iTunes.
Is there a way since (iOS 7's release) to get the UDID without using iTunes on a PC/Mac?
I had got some issue too, and solution is simple:
// Get Bundle Info for Remote Registration (handy if you have more than one app)
NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleDisplayName"];
NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"];
// Get the users Device Model, Display Name, Unique ID, Token & Version Number
UIDevice *dev = [UIDevice currentDevice];
NSString *deviceUuid=[dev.identifierForVendor UUIDString];
NSString *deviceName = dev.name;
A not perfect but one of the best and closest alternative to UDID (in Swift using iOS 8.1 and Xcode 6.1):
Generating a random UUID
let strUUID: String = NSUUID().UUIDString
And use KeychainWrapper library:
Add a string value to keychain:
let saveSuccessful: Bool = KeychainWrapper.setString("Some String", forKey: "myKey")
Retrieve a string value from keychain:
let retrievedString: String? = KeychainWrapper.stringForKey("myKey")
Remove a string value from keychain:
let removeSuccessful: Bool = KeychainWrapper.removeObjectForKey("myKey")
This solution uses the keychain, thus the record stored in the keychain will be persisted, even after the app is uninstalled and reinstalled. The only way of deleting this record is to Reset all contents and settings of the device. That is why I mentioned that this solution of substitution is not perfect but stays one of the best solution of replacement for UDID on iOS 8.1 using Swift.
NSLog(#"%#",[[UIDevice currentDevice]identifierForVendor]);

iPhone get SSID without private library

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?
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
}
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 networkextension#apple.com 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.
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;
}
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;
}
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>;
}
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
See CNCopyCurrentNetworkInfo in CaptiveNetwork: http://developer.apple.com/library/ios/#documentation/SystemConfiguration/Reference/CaptiveNetworkRef/Reference/reference.html.
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.
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:
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];
}
}
}
...
...
}

Resources