As you know Apple recently deprecated the usage of UDID. So my solution to this was
Generate CFUUID
Save it to keychain
Re-access the keychain item there after.
This has been working good. But, for some reason we recently saw that with the installation of an enterprise build we are getting a different UUID(Which was supposed to be stored on keychain with our unique access key).
Did any one come across such situation? Here is the code to create the UUID and store it onto keychain..
+ (NSString *)registerUUIDWithKeyChain
{
CFUUIDRef udid = CFUUIDCreate(NULL);
NSString *uuidString = (NSString *) CFUUIDCreateString(NULL, udid);
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"UniqueApp" accessGroup:nil];
NSString *userName = #"UniqueAppName";
NSString *password = uuidString;
[keychainItem setObject:userName forKey:(id)kSecAttrAccount];
[keychainItem setObject:password forKey:(id)kSecValueData];
[keychainItem release];
return uuidString;
}
+ (NSString *)userUUID
{
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"UniqueApp" accessGroup:nil];
//Accesing the v_data was the only way. For some reason there is a runtime issue if we try to access it though "kSecValueData"
NSString *uuid = [keychainItem.keychainItemData objectForKey:#"v_Data"];
//Check if the app is installed for the first time on the device. If YES register the UUID in to the keychain.
//Also check if it is a reinstall by accessing the previous keyChainItem with our Identifier.
if ([[[NSUserDefaults standardUserDefaults] valueForKey:#"firstRun"] intValue] == 0 && !(uuid.length > 0))
{
uuid = [UIDevice_Additions registerUUIDWithKeyChain];
NSLog(#"\n First Time Registered UUID is %#", uuid);
//after stuff done
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:1] forKey:#"firstRun"];
[[NSUserDefaults standardUserDefaults] synchronize];
[keychainItem release];
return uuid;
}
[keychainItem release];
return uuid;
}
#end
Okie,
After battling with the issue for a day, I found what was triggering this.
Keychain's are certificate dependent
An enterprise build is created with a different certificate
Hence, when the code tries to access your key from an enterprise build you will not find it and hence the code generates will generate a new one.
Solution would be to create your Keychain so that it is globally accessible. You can change the accessGroup variable in the KeyChainWrapper init method.
Good Luck!
you may use MAC Addres instread of UUID
- (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;
}
Related
I want to use an alternative to the UDID and found this:
+ (NSString *)GetUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString *)string autorelease];
}
but in the simulator the method gives me different results every session?
Is this only in simulator?
I need to be sure that on actual devices the method returns me always the same string
to identify a user.
Is it true or not?
Mirza
CFUUIDRef will create different values at each session.
Solution 1:
Save the value in NSUserDefaults and next time onwards use it from the NSUserDefaults.
Solution 2:
You can use identifierForVendor for doing this.
NSString *udidVendor = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
According to UIDevice Class Reference:
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.
Please check Unique Identifier In iOS 6
CFUUIDCreate gives you a Universally Unique Identifier every time you call that function, so each time you will get a different result (by definition).
What you can do is persist this in between sessions using, for example, NSUserDefaults, to uniquely identify a particular user (or bunch of user's settings).
CFUUID is not persisted at all.
Every time you call CFUUIDCreate the system will return to you a brand new unique identifier.
If you want to persist this identifier you will need to do that yourself using NSUserDefaults, Keychain, Pasteboard or some other means.
Read the code one line at a time and try to understand what it does. The CFUUIDCreate function creates a new UUID every time you call it. That would explain your finding. You need to save the value in NSUserDefaults* the first time and use that value the next time you launch the app:
+ (NSString *)GetUUID
{
NSString *string = [[NSUserDefaults standardUserDefaults] objectForKey: #"UUID"];
if (!string)
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
string = (NSString*)[CFUUIDCreateString(NULL, theUUID) autorelease];
CFRelease(theUUID);
[[NSUserDefaults standardUserDefaults] setObject: string forKey: #"UUID"];
}
return string;
}
*There are one small caveat of using NSUserDefaults - UUID will be created again if user uninstalls and reinstalls the app again. If you can't live with this, look into saving it in Keychain. Alternatively, you might want to look at OpenUDID.
The method will always return a unique string. If the app will only ever have a single user, run this method once when the user first launches the app and persist that string in a plist, or NSUserDefaults, or core data if you've already using it.
The link below may help with this UUID persistence logic:
UUID for app on iOS5
However, if the user then uninstalls and reinstalls the app, this persisted UUID will still be lost and need will be generated again.
Device IDs are also no longer allowed by Apple.
Assuming the UUID is required because the app connects to a server, as far as I know, you need the user log in to the server with a user name and password.
It is always different. UUID includes timestamps, so every time you call this function, you will get a different (random) one.
I have followed this approach in IDManager class,
This is a collection from different solutions. KeyChainUtil is a wrapper to read from keychain. A similar keychain util is found in github.
// IDManager.m
/*
A replacement for deprecated uniqueIdentifier API. Apple restrict using this from 1st May, 2013.
We have to consider,
* iOS <6 have not the ASIIdentifer API
* When the user upgrade from iOS < 6 to >6
- Check if there is a UUID already stored in keychain. Then use that.
- In that case, this UUID is constant for whole device lifetime. Keychain item is not deleted with application deletion.
*/
#import "IDManager.h"
#import "KeychainUtils.h"
#import "CommonUtil.h"
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
#import <AdSupport/AdSupport.h>
#endif
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
/* 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"
#pragma mark
#pragma mark
#implementation IDManager
+ (NSString *) getUniqueID {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
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 {
#endif
return [IDManager getUniqueUUID];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
}
#endif
}
+ (NSString *) getUniqueUUID
{
NSError * error;
NSString * uuid = [KeychainUtils getPasswordForUsername:#"UserName" andServiceName:#"YourServiceName" 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 = [CommonUtil md5String:uuid]; // create md5 hash for security reason
[KeychainUtils storeUsername:#"UserName" andPassword:uuid forServiceName:#"YourServiceName" updateExisting:YES error:&error];
if (error) {
NSLog(#"Error geting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
}
return uuid;
}
+ (NSString *) readUUIDFromKeyChain {
NSError * error;
NSString * uuid = [KeychainUtils getPasswordForUsername:#"UserName" andServiceName:#"YourServiceName" error:&error];
if (error) {
NSLog(#"Error geting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
return uuid;
}
/* NSUUID is after iOS 6. So we are using CFUUID for compatibility with iOS 4.3 */
+ (NSString *)getUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString *)string autorelease];
}
#pragma mark - MAC address
/* THIS WILL NOT WORK IN iOS 7. IT WILL RETURN A CONSTANT MAC ADDRESS ALL THE TIME.
SEE - https://developer.apple.com/news/?id=8222013a
*/
// Return the local MAC address
// 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 [CommonUtil md5String:mac];
}
#end
I developing iPhone application. In this app i need to get device udid. In simulator working fine, but in iphone device not getting correct value, the values getting 0.
The code are following;
UIDevice *device = [UIDevice currentDevice];
NSString *uniqueIdentifier = [device uniqueIdentifier];
Thanks,
As we know uniqueIdentifier is deprecated in iOS 5.0 so docs recommend you to use CFUUID
instead. You can get CFUUID using
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
NSString *uuidString = (NSString *)CFUUIDCreateString(NULL,uuidRef);
CFRelease(uuidRef);
Please save the uuidString in user defaults or in other place because you can not generate the same uuidString again.
You can use mac address also in place of this as an alternative how-can-i-programmatically-get-the-mac-address-of-an-iphone
Hope it helps you.
UDID is deprecated. From 1st May 2013, Apple is not approving any app that access uniqueIdentifier. Instead you can use ASIIDentifier or NSUUID.Or MAC address.
I have followed this approach in IDManager class,
This is a collection from different solutions. KeyChainUtil is a wrapper to read from keychain.
#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 getUniqueID];
} 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:kBuyassUser andPassword:uuid forServiceName:kIdOgBetilngService updateExisting:YES error:&error];
if (error) {
NSLog(#"Error geting 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;
}
I just tried the hyphenate library of Tupil.
It was mentioned here http://blog.tupil.com/adding-hyphenation-to-nsstring/.
But while it is working perfectly under iOS 4.3, I did not get it to work with iOS 5.
Are there any other frameworks I could use? I heard of CoreText, but I don't know where to start.
Thanks in advance
Martin
I realize it's been a few years, but I just found that there's a Core Foundation function that suggests hyphenation points: CFStringGetHyphenationLocationBeforeIndex. It only works for a few languages, but it looks like it might be really helpful for the narrow label problem.
Update:
Here is some example code. It's a CLI program that shows where to hyphenate a word:
#include <Cocoa/Cocoa.h>
int main(int ac, char *av[])
{
#autoreleasepool {
if(ac < 2) {
fprintf(stderr, "usage: hyph word\n");
exit(1);
}
NSString *word = [NSString stringWithUTF8String: av[1]];
unsigned char hyspots[word.length];
memset(hyspots, 0, word.length);
CFRange range = CFRangeMake(0, word.length);
CFLocaleRef locale = CFLocaleCreate(NULL, CFSTR("en_US"));
for(int i = 0; i < word.length; i++) {
int x = CFStringGetHyphenationLocationBeforeIndex(
(CFStringRef) word, i, range,
0, locale, NULL);
if(x >= 0 && x < word.length)
hyspots[x] = 1;
}
for(int i = 0; i < word.length; i++) {
if(hyspots[i]) putchar('-');
printf("%s", [[word substringWithRange: NSMakeRange(i, 1)] UTF8String]);
}
putchar('\n');
}
exit(0);
}
Here's how it looks when you build and run it:
$ cc -o hyph hyph.m -framework Cocoa
$ hyph accessibility
ac-ces-si-bil-i-ty
$ hyph hypothesis
hy-poth-e-sis
These hyphenations agree exactly with the OS X dictionary. I am using this for a narrow label problem in iOS, and it's working well for me.
I wrote a category based Jeffrey's answer for adding "soft hyphenation" to any string. These are "-" which is not visible when rendered, but instead merely queues for CoreText or UITextKit to know how to break up words.
NSString *string = #"accessibility tests and frameworks checking";
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:#"en_US"];
NSString *hyphenatedString = [string softHyphenatedStringWithLocale:locale error:nil];
NSLog(#"%#", hyphenatedString);
Outputs ac-ces-si-bil-i-ty tests and frame-works check-ing
NSString+SoftHyphenation.h
typedef enum {
NSStringSoftHyphenationErrorNotAvailableForLocale
} NSStringSoftHyphenationError;
extern NSString * const NSStringSoftHyphenationErrorDomain;
extern NSString * const NSStringSoftHyphenationToken;
#interface NSString (SoftHyphenation)
- (BOOL)canSoftHyphenateStringWithLocale:(NSLocale *)locale;
- (NSString *)softHyphenatedStringWithLocale:(NSLocale *)locale error:(out NSError **)error;
#end
NSString+SoftHyphenation.m
#import "NSString+SoftHyphenation.h"
NSString * const NSStringSoftHyphenationErrorDomain = #"NSStringSoftHyphenationErrorDomain";
NSString * const NSStringSoftHyphenationToken = #""; // NOTE: UTF-8 soft hyphen!
#implementation NSString (SoftHyphenation)
- (BOOL)canSoftHyphenateStringWithLocale:(NSLocale *)locale
{
CFLocaleRef localeRef = (__bridge CFLocaleRef)(locale);
return CFStringIsHyphenationAvailableForLocale(localeRef);
}
- (NSString *)softHyphenatedStringWithLocale:(NSLocale *)locale error:(out NSError **)error
{
if(![self canSoftHyphenateStringWithLocale:locale])
{
if(error != NULL)
{
*error = [self hyphen_createOnlyError];
}
return [self copy];
}
else
{
NSMutableString *string = [self mutableCopy];
unsigned char hyphenationLocations[string.length];
memset(hyphenationLocations, 0, string.length);
CFRange range = CFRangeMake(0, string.length);
CFLocaleRef localeRef = (__bridge CFLocaleRef)(locale);
for(int i = 0; i < string.length; i++)
{
CFIndex location = CFStringGetHyphenationLocationBeforeIndex((CFStringRef)string, i, range, 0, localeRef, NULL);
if(location >= 0 && location < string.length)
{
hyphenationLocations[location] = 1;
}
}
for(int i = string.length - 1; i > 0; i--)
{
if(hyphenationLocations[i])
{
[string insertString:NSStringSoftHyphenationToken atIndex:i];
}
}
if(error != NULL) { *error = nil; }
// Left here in case you want to test with visible results
// return [string stringByReplacingOccurrencesOfString:NSStringSoftHyphenationToken withString:#"-"];
return string;
}
}
- (NSError *)hyphen_createOnlyError
{
NSDictionary *userInfo = #{
NSLocalizedDescriptionKey: #"Hyphenation is not available for given locale",
NSLocalizedFailureReasonErrorKey: #"Hyphenation is not available for given locale",
NSLocalizedRecoverySuggestionErrorKey: #"You could try using a different locale even though it might not be 100% correct"
};
return [NSError errorWithDomain:NSStringSoftHyphenationErrorDomain code:NSStringSoftHyphenationErrorNotAvailableForLocale userInfo:userInfo];
}
#end
:)
How to programmatically get an iPhone's MAC address and IP address?
NOTE As of iOS7, you can no longer retrieve device MAC addresses. A fixed value will be returned rather than the actual MAC
Somthing I stumbled across a while ago. Originally from here I modified it a bit and cleaned things up.
IPAddress.h
IPAddress.c
And to use it
InitAddresses();
GetIPAddresses();
GetHWAddresses();
int i;
NSString *deviceIP = nil;
for (i=0; i<MAXADDRS; ++i)
{
static unsigned long localHost = 0x7F000001; // 127.0.0.1
unsigned long theAddr;
theAddr = ip_addrs[i];
if (theAddr == 0) break;
if (theAddr == localHost) continue;
NSLog(#"Name: %s MAC: %s IP: %s\n", if_names[i], hw_addrs[i], ip_names[i]);
//decided what adapter you want details for
if (strncmp(if_names[i], "en", 2) == 0)
{
NSLog(#"Adapter en has a IP of %s", ip_names[i]);
}
}
Adapter names vary depending on the simulator/device as well as wifi or cell on the device.
Update: this will not work on iOS 7. You should use ASIdentifierManager.
More clean solution on MobileDeveloperTips website:
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
...
- (NSString *)getMacAddress
{
int mgmtInfoBase[6];
char *msgBuffer = NULL;
size_t length;
unsigned char macAddress[6];
struct if_msghdr *interfaceMsgStruct;
struct sockaddr_dl *socketStruct;
NSString *errorFlag = NULL;
// Setup the management Information Base (mib)
mgmtInfoBase[0] = CTL_NET; // Request network subsystem
mgmtInfoBase[1] = AF_ROUTE; // Routing table info
mgmtInfoBase[2] = 0;
mgmtInfoBase[3] = AF_LINK; // Request link layer information
mgmtInfoBase[4] = NET_RT_IFLIST; // Request all configured interfaces
// With all configured interfaces requested, get handle index
if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0)
errorFlag = #"if_nametoindex failure";
else
{
// Get the size of the data available (store in len)
if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0)
errorFlag = #"sysctl mgmtInfoBase failure";
else
{
// Alloc memory based on above call
if ((msgBuffer = malloc(length)) == NULL)
errorFlag = #"buffer allocation failure";
else
{
// Get system information, store in buffer
if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
errorFlag = #"sysctl msgBuffer failure";
}
}
}
// Befor going any further...
if (errorFlag != NULL)
{
NSLog(#"Error: %#", errorFlag);
return errorFlag;
}
// Map msgbuffer to interface message structure
interfaceMsgStruct = (struct if_msghdr *) msgBuffer;
// Map to link-level socket structure
socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
// Copy link layer address data in socket structure to an array
memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);
// Read from char array into a string object, into traditional Mac address format
NSString *macAddressString = [NSString stringWithFormat:#"%02X:%02X:%02X:%02X:%02X:%02X",
macAddress[0], macAddress[1], macAddress[2],
macAddress[3], macAddress[4], macAddress[5]];
NSLog(#"Mac Address: %#", macAddressString);
// Release the buffer memory
free(msgBuffer);
return macAddressString;
}
I wanted something to return the address regardless of whether or not wifi was enabled, so the chosen solution didn't work for me. I used another call I found on some forum after some tweaking. I ended up with the following (excuse my rusty C ) :
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <net/if_dl.h>
#include <ifaddrs.h>
char* getMacAddress(char* macAddress, char* ifName) {
int success;
struct ifaddrs * addrs;
struct ifaddrs * cursor;
const struct sockaddr_dl * dlAddr;
const unsigned char* base;
int i;
success = getifaddrs(&addrs) == 0;
if (success) {
cursor = addrs;
while (cursor != 0) {
if ( (cursor->ifa_addr->sa_family == AF_LINK)
&& (((const struct sockaddr_dl *) cursor->ifa_addr)->sdl_type == IFT_ETHER) && strcmp(ifName, cursor->ifa_name)==0 ) {
dlAddr = (const struct sockaddr_dl *) cursor->ifa_addr;
base = (const unsigned char*) &dlAddr->sdl_data[dlAddr->sdl_nlen];
strcpy(macAddress, "");
for (i = 0; i < dlAddr->sdl_alen; i++) {
if (i != 0) {
strcat(macAddress, ":");
}
char partialAddr[3];
sprintf(partialAddr, "%02X", base[i]);
strcat(macAddress, partialAddr);
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
return macAddress;
}
And then I would call it asking for en0, as follows:
char* macAddressString= (char*)malloc(18);
NSString* macAddress= [[NSString alloc] initWithCString:getMacAddress(macAddressString, "en0")
encoding:NSMacOSRomanStringEncoding];
free(macAddressString);
Starting from iOS 7, the system always returns the value 02:00:00:00:00:00 when you ask for the MAC address on any device.
In iOS 7 and later, if you ask for the MAC address of an iOS device, the system returns the value 02:00:00:00:00:00. If you need to identify the device, use the identifierForVendor property of UIDevice instead. (Apps that need an identifier for their own advertising purposes should consider using the advertisingIdentifier property of ASIdentifierManager instead.)"
Reference: releasenotes
There are vary solutions about this, but I couldn't find a whole thing.
So I made my own solution for :
nicinfo
How to use :
NICInfoSummary* summary = [[[NICInfoSummary alloc] init] autorelease];
// en0 is for WiFi
NICInfo* wifi_info = [summary findNICInfo:#"en0"];
// you can get mac address in 'XX-XX-XX-XX-XX-XX' form
NSString* mac_address = [wifi_info getMacAddressWithSeparator:#"-"];
// ip can be multiple
if(wifi_info.nicIPInfos.count > 0)
{
NICIPInfo* ip_info = [wifi_info.nicIPInfos objectAtIndex:0];
NSString* ip = ip_info.ip;
NSString* netmask = ip_info.netmask;
NSString* broadcast_ip = ip_info.broadcastIP;
}
else
{
NSLog(#"WiFi not connected!");
}
This looks like a pretty clean solution: UIDevice BIdentifier
// 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");
free(buf);
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;
}
Now iOS 7 devices – are always returning a MAC address of 02:00:00:00:00:00.
So better use [UIDevice identifierForVendor].
so better to call this method to get app specific unique key
Category will more suitable
import "UIDevice+Identifier.h"
- (NSString *) identifierForVendor1
{
if ([[UIDevice currentDevice] respondsToSelector:#selector(identifierForVendor)]) {
return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
return #"";
}
Now call above method to get unique address
NSString *like_UDID=[NSString stringWithFormat:#"%#",
[[UIDevice currentDevice] identifierForVendor1]];
NSLog(#"%#",like_UDID);
#Grantland
This "pretty clean solution" looks similar to my own improvement over iPhoneDeveloperTips solution.
You can see my step here:
https://gist.github.com/1409855/
/* Original source code courtesy John from iOSDeveloperTips.com */
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
+ (NSString *)getMacAddress
{
int mgmtInfoBase[6];
char *msgBuffer = NULL;
NSString *errorFlag = NULL;
size_t length;
// Setup the management Information Base (mib)
mgmtInfoBase[0] = CTL_NET; // Request network subsystem
mgmtInfoBase[1] = AF_ROUTE; // Routing table info
mgmtInfoBase[2] = 0;
mgmtInfoBase[3] = AF_LINK; // Request link layer information
mgmtInfoBase[4] = NET_RT_IFLIST; // Request all configured interfaces
// With all configured interfaces requested, get handle index
if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0)
errorFlag = #"if_nametoindex failure";
// Get the size of the data available (store in len)
else if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0)
errorFlag = #"sysctl mgmtInfoBase failure";
// Alloc memory based on above call
else if ((msgBuffer = malloc(length)) == NULL)
errorFlag = #"buffer allocation failure";
// Get system information, store in buffer
else if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
{
free(msgBuffer);
errorFlag = #"sysctl msgBuffer failure";
}
else
{
// Map msgbuffer to interface message structure
struct if_msghdr *interfaceMsgStruct = (struct if_msghdr *) msgBuffer;
// Map to link-level socket structure
struct sockaddr_dl *socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
// Copy link layer address data in socket structure to an array
unsigned char macAddress[6];
memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);
// Read from char array into a string object, into traditional Mac address format
NSString *macAddressString = [NSString stringWithFormat:#"%02X:%02X:%02X:%02X:%02X:%02X",
macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]];
NSLog(#"Mac Address: %#", macAddressString);
// Release the buffer memory
free(msgBuffer);
return macAddressString;
}
// Error...
NSLog(#"Error: %#", errorFlag);
return nil;
}
It's not possible anymore on devices running iOS 7.0 or later, thus unavailable to get MAC address in Swift.
As Apple stated:
In iOS 7 and later, if you ask for the MAC address of an iOS device, the system returns the value 02:00:00:00:00:00. If you need to identify the device, use the identifierForVendor property of UIDevice instead. (Apps that need an identifier for their own advertising purposes should consider using the advertisingIdentifier property of ASIdentifierManager instead.)
#import <sys/socket.h>
#import <net/if_dl.h>
#import <ifaddrs.h>
#import <sys/xattr.h>
#define IFT_ETHER 0x6
...
- (NSString*)macAddress
{
NSString* result = nil;
char* macAddressString = (char*)malloc(18);
if (macAddressString != NULL)
{
strcpy(macAddressString, "");
struct ifaddrs* addrs = NULL;
struct ifaddrs* cursor;
if (getifaddrs(&addrs) == 0)
{
cursor = addrs;
while (cursor != NULL)
{
if ((cursor->ifa_addr->sa_family == AF_LINK) && (((const struct sockaddr_dl*)cursor->ifa_addr)->sdl_type == IFT_ETHER) && strcmp("en0", cursor->ifa_name) == 0)
{
const struct sockaddr_dl* dlAddr = (const struct sockaddr_dl*) cursor->ifa_addr;
const unsigned char* base = (const unsigned char*)&dlAddr->sdl_data[dlAddr->sdl_nlen];
for (NSInteger index = 0; index < dlAddr->sdl_alen; index++)
{
char partialAddr[3];
sprintf(partialAddr, "%02X", base[index]);
strcat(macAddressString, partialAddr);
}
}
cursor = cursor->ifa_next;
}
}
result = [[[NSString alloc] initWithUTF8String:macAddressString] autorelease];
free(macAddressString);
}
return result;
}
To create a uniqueString based on unique identifier of device in iOS 6:
#import <AdSupport/ASIdentifierManager.h>
NSString *uniqueString = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
NSLog(#"uniqueString: %#", uniqueString);
A lot of these questions only address the Mac address. If you also require the IP address I just wrote this, may need some work but seems to work well on my machine...
- (NSString *)getLocalIPAddress
{
NSArray *ipAddresses = [[NSHost currentHost] addresses];
NSArray *sortedIPAddresses = [ipAddresses sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.allowsFloats = NO;
for (NSString *potentialIPAddress in sortedIPAddresses)
{
if ([potentialIPAddress isEqualToString:#"127.0.0.1"]) {
continue;
}
NSArray *ipParts = [potentialIPAddress componentsSeparatedByString:#"."];
BOOL isMatch = YES;
for (NSString *ipPart in ipParts) {
if (![numberFormatter numberFromString:ipPart]) {
isMatch = NO;
break;
}
}
if (isMatch) {
return potentialIPAddress;
}
}
// No IP found
return #"?.?.?.?";
}
Wow... look at all the "panic stories" online this week regarding using an iPhone's UDID.
[[UIDevice currentDevice] uniqueIdentifier]
What SHOULD we be using instead?
What if the phone is sold to another user... and an app has stored some data on a remote server, based on the phone's UDID?
(Of course, I want to avoid the problems with the app store's "encryption restrictions".)
Why not use the Mac Address and possibly then hash it up.
There is an excellent UIDevice-Extension Category here
- (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)];
// 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;
}
You could possibly hash this with the model?
As I asked this morning in this post, there are some alternative :
1- first, as Apple recommands, identify per install instead of indentifying per device.
Therefore, you can use CFUUIDRef. Example :
NSString *uuid = nil;
CFUUIDRef theUUID = CFUUIDCreate(kCFAllocatorDefault);
if (theUUID) {
uuid = NSMakeCollectable(CFUUIDCreateString(kCFAllocatorDefault, theUUID));
[uuid autorelease];
CFRelease(theUUID);
}
2- If you care about a worldwide unique identifier, so you could store this identifier on iCloud.
3- At last, if you really need an identifier that remains after app re-install (that not occurs so frequently), you can use Keychains (Apple's keychain doc).
But will apple team like it ?
UUID is just depreciated and so will be around for a while, Apple have not said much about this depreciation much yet, I would wait until they have more to say about this and maybe the will offer some alternative.
Like this:
#interface UIDevice (UIDeviceAppIdentifier)
#property (readonly) NSString *deviceApplicationIdentifier;
#end
#implementation UIDevice (UIDeviceAppIdentifier)
- (NSString *) deviceApplicationIdentifier
{
static NSString *name = #"theDeviceApplicationIdentifier";
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *value = [defaults objectForKey: name];
if (!value)
{
value = (NSString *) CFUUIDCreateString (NULL, CFUUIDCreate(NULL));
[defaults setObject: value forKey: name];
[defaults synchronize];
}
return value;
}
#end
the iOS documentation more or less describes use of CFUUIDCreate() to create an identifier and suggests using UserDefaults to store it.
The recommended way is by using UUID generation, and associate that with something that the user him/herself is willing to provide to the app.
Then, store this data externally, where it could be retrieved again. There are probably other ways to do this easily, but this is the recommended way.
One solution would be to have the application issue a free in-app purchase.
This purchase would be:
Trackable, with a unique number (purchase) number which would be meaningful only to your app.
Movable, if the person switches devices
Retrievable, if the app is deleted (or the phone is wiped and reloaded) - the In-App purchases can be restored.
Apple's documentation says:
"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."
Here's a quick snippet:
CFUUIDRef udid = CFUUIDCreate(NULL);
NSString *udidString = (NSString *) CFUUIDCreateString(NULL, udid);