I need to generate some int value that would never repeat (at least theoretically). I know there is arc4random() fnc but I'm not sure how to use it with some current date or smth :(
This returns a unique key very similar to UUID generated in MySQL.
+ (NSString *)uuid
{
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
return [(NSString *)uuidStringRef autorelease];
}
ARC version:
+ (NSString *)uuid
{
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
return (__bridge_transfer NSString *)uuidStringRef;
}
A simple version to generate UUID (iOS 6 or later).
Objective-C:
NSString *UUID = [[NSUUID UUID] UUIDString];
Swift 3+:
let uuid = UUID().uuidString
It will generate something like 68753A44-4D6F-1226-9C60-0050E4C00067, which is unique every time you call this function, even across multiple devices and locations.
Reference:
https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUUID_Class/Reference/Reference.html
If you are using CoreData to save the played games, NSManagedObject's objectID should serve your purpose without any extra effort.
You can use the time in milliseconds or a more advanced way GUID.
You can create a category of UIApplication , UIDevice or as you prefere like this (ARC example)
#interface UIApplication (utilities)
- (NSString*)getUUID;
#end
#implementation UIApplication (utilities)
- (NSString*)getUUID {
NSUserDefaults *standardUserDefault = [NSUserDefaults standardUserDefaults];
static NSString *uuid = nil;
// try to get the NSUserDefault identifier if exist
if (uuid == nil) {
uuid = [standardUserDefault objectForKey:#"UniversalUniqueIdentifier"];
}
// if there is not NSUserDefault identifier generate one and store it
if (uuid == nil) {
uuid = UUID ();
[standardUserDefault setObject:uuid forKey:#"UniversalUniqueIdentifier"];
[standardUserDefault synchronize];
}
return uuid;
}
#end
UUID () is this function
NSString* UUID () {
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
return (__bridge NSString *)uuidStringRef;
}
this generate an unique identifier stored into the NSUserDefault to be reused whenever the application need it - This identifier will unique related to the application installs not to the device, but can be used for example to take trace about the number devices subscribed the APN service etc...
After that you can use it in this way:
NSString *uuid = [[UIApplication sharedApplication] getUUID];
A simple timestamp (milliseconds * 10) should do the trick:
self.uid = [NSNumber numberWithInteger:[NSDate timeIntervalSinceReferenceDate] * 10000];
You did not say it must be random. So why not start with some number, and then just add by 1 to the last number you generated.
This method should give you at lest 4 billion unique numbers to start with:
-(NSInteger)nextIdentifies;
{
static NSString* lastID = #"lastID";
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSInteger identifier = [defaults integerForKey:lastID] + 1;
[defaults setInteger:identifier forKey:lastID];
[defaults synchronize];
return identifier;
}
If you have a NSDictionary, you could generate a progressive id from the last item:
NSInteger maxKey = -1;
for(NSString *key in [YOUR_DICTIONARY allKeys])
{
NSInteger intKey = [key integerValue];
if(intKey > maxKey)
{
maxKey = intKey;
}
}
NSString *newKey = [NSString stringWithFormat:#"%d", maxKey + 1];
You have to be careful, especially if you use the increment by 1 routines, that if your app is deleted and reloaded on the iDevice, that you won't have your saved default number anymore. It will start over from the beginning. If you're storing user's scores, you might want to save their highest number too. Better to check the time routines for seconds (or milliseconds) after a certain date. The GUID mentioned above is good too, if you need that kind of uniqueness.
Related
I´m a beginer and have lately got a great deal of trouble with this issue.
I want to pass a NSDictnonary Data to a server from my app and in some cases if the user hasen´t chosen any option I want to remove nil objects.
I have looked into this thread that seems be the right method but I haven't succeed to implement in my code.
How to remove a null value from NSDictionary
My guess would be to implement the code to Null directly in my NSDictonary
Here´s my Dictionary code
-(NSDictionary*)parametersForCreateActivities
{
NSString *token = [[A0SimpleKeychain keychain] stringForKey:tokenConstant];
NSString *userId = [[A0SimpleKeychain keychain] stringForKey:child_id];
NSString *userCreate = [[NSUserDefaults standardUserDefaults] objectForKey:#"CreateTitle"];
NSString *createDescription = [[NSUserDefaults standardUserDefaults] objectForKey:#"DescriptionText"];
NSString *createTimestart = [[NSUserDefaults standardUserDefaults] objectForKey:#"TimeStartString"];
NSString *createTimestop = [[NSUserDefaults standardUserDefaults] objectForKey:#"TimeStopString"];
NSString *createImage = [[NSUserDefaults standardUserDefaults] objectForKey:#"DefaultcreateImageID"];
NSDictionary *parameters;
if (userId && token) {
parameters = #{child_id: userId, tokenConstant:token, activity_name :userCreate, create_Description :createDescription, create_Timestart :createTimestart, create_Timestop :createTimestop, create_Image :createImage};
}
return parameters;
}
My guess is that It should check somewhere in the code for nil objects and remove theme. But I have really struggled with figuring out how to format the code.
I´m guessing the code should be something like this but I have no idea where to place it and how to format it.
NSMutableDictionary *dict = [parametersForCreateActivities mutableCopy];
NSArray *keysForNullValues = [dict allKeysForObject:[NSNull null]];
[dict removeObjectsForKeys:DefaultcreateImageID];
Try below code
NSMutableDictionary *yourDictionary; // Your dictionary object with data
NSMutableDictionary *updatedDic = [yourDictionary mutableCopy];
for (NSString *key in [yourDictionary allKeys]) {
if ([yourDictionary[key] isEqual:[NSNull null]] || [yourDictionary[key] isKindOfClass:[NSNull class]]) {
updatedDic[key] = #"";
}
}
yourDictionary = [updatedDic copy];
NSLog(#"%#",yourDictionary);
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
When I launch an app, I need to retrieve all contacts from Address Book & store it in array to display it in table view. I wrote this code in viewDidAppear method. While contacts are retrieving from Address Book , I am showing activity indicator. I have around 1100 contacts in my address book. For this it took 14 seconds to retrieve data & store it in array. Its not acceptable time. So I need to optimize this & reduce time to max 2 to 3 seconds.
I need all contacts because when my app launches , I need to search for contacts so I need all the data available in my array.
How can I reduce this timing ? If you need more information just let me know.
Any kind of help is highly appreciated. Thanks.
UPDATE 1 : My code
- (NSMutableArray*)getAddressBookData {
self.tempArray = [[NSMutableArray alloc]init];
addressBook = ABAddressBookCreate();
APP_DELGATE.people = (NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
peopleCount = [APP_DELGATE.people count];
for (int i=0; i<peopleCount; i++) {
ABRecordRef record = [APP_DELGATE.people objectAtIndex:i];
NSNumber *recordId = [NSNumber numberWithInteger:ABRecordGetRecordID(record)];
NSLog(#"record id is %#",recordId);
// Get fname, lname, company
NSString *fnm = (NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty) ;
NSString *lnm = (NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty) ;
NSString *comp = (NSString*)ABRecordCopyValue(record,kABPersonOrganizationProperty);
// Get Ph no
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(record, kABPersonPhoneProperty);
NSArray* phoneNumbers = [self getPhoneNoWithoutSymbols:(NSArray*)ABMultiValueCopyArrayOfAllValues(phoneNumberProperty)];
NSString *strPhoneNos = [self getStringRepresentaionFromArray:phoneNumbers];
NSLog(#"strPhoneNos => %#",strPhoneNos);
// Get emails
ABMultiValueRef emailProperty = ABRecordCopyValue(record, kABPersonEmailProperty);
NSArray* emails = (NSArray*)ABMultiValueCopyArrayOfAllValues(emailProperty);
NSString *strEmails = [self getStringRepresentaionFromArray:emails];
NSLog(#"strEmails => %#",strEmails);
// Get URL
ABMultiValueRef urlProperty = ABRecordCopyValue(record, kABPersonURLProperty);
NSArray* urls = (NSArray*)ABMultiValueCopyArrayOfAllValues(urlProperty);
NSString *strURLs = [self getStringRepresentaionFromArray:urls];
NSLog(#"strURLs => %#",strURLs);
// Get Address
ABMultiValueRef address=ABRecordCopyValue(record, kABPersonAddressProperty);
CFDictionaryRef dic=nil;
NSMutableArray *addressArray = [[NSMutableArray alloc]init];
for (int index=0; index<ABMultiValueGetCount(address); index++) {
dic=ABMultiValueCopyValueAtIndex(address, index);
NSString* labelName=(NSString*)ABMultiValueCopyLabelAtIndex(address, index);
if (labelName) {
NSString *street =(NSString*) CFDictionaryGetValue(dic, kABPersonAddressStreetKey);
NSString *city= (NSString*)CFDictionaryGetValue(dic, kABPersonAddressCityKey) ;
NSString *state= CFDictionaryGetValue(dic, kABPersonAddressStateKey);
NSString *country=CFDictionaryGetValue(dic, kABPersonAddressCountryKey);
NSString *zipcode=CFDictionaryGetValue(dic, kABPersonAddressZIPKey);
NSString *addressDetails=#"";
if (street) {
addressDetails=[NSString stringWithFormat:#"%# ",street];
}
if (city) {
addressDetails=[NSString stringWithFormat:#"%# %# ",addressDetails,city];
}
if (state) {
addressDetails=[NSString stringWithFormat:#"%# %# ",addressDetails,state];
}
if (country) {
addressDetails=[NSString stringWithFormat:#"%# %# ",addressDetails,country];
}
if (zipcode) {
addressDetails=[NSString stringWithFormat:#"%# %# ",addressDetails,zipcode];
}
[addressArray addObject:addressDetails];
}
}
NSString *strAddress = [self getStringRepresentaionFromArray:addressArray];
NSLog(#"strAddress => %#",strAddress);
// Get Notes
NSString *noteString=(NSString *)ABRecordCopyValue(record, kABPersonNoteProperty);
// Get Birthdate
NSDate *birthDate=(NSDate*)ABRecordCopyValue(record, kABPersonBirthdayProperty) ;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMMM dd yyyy"];
NSString *birthdateString = [formatter stringFromDate:birthDate];
[formatter release];
// Get user image
UIImage *image = nil;
if( ABPersonHasImageData( record ) ) {
NSData *imageData = (NSData*)ABPersonCopyImageData(record);
image = [UIImage imageWithData:imageData];
[imageData release];
}
// Create User object & add it to array
User *user = [[User alloc]initUserWithUniqID:recordId.intValue FirstName:fnm lastName:lnm company:comp phoneNumbers:strPhoneNos emails:strEmails urls:strURLs address:strAddress notes:noteString dob:birthdateString userImage:image];
[self.tempArray addObject:user];
[user release];
}
self.tempArray = [NSMutableArray arrayWithArray:[self.tempArray sortedArrayUsingSelector:#selector(compare:)]];
APP_DELGATE.allUsersArray = self.tempArray;
return tempArray;
}
-(NSMutableArray*)getPhoneNoWithoutSymbols:(NSArray*)array {
self.phNoArray = [[NSMutableArray alloc]init];
for (NSString *str in array) {
[self.phNoArray addObject:[self getPhNo:str]];
}
return self.phNoArray;
}
-(NSString*)getPhNo:(NSString*)str {
NSString *str0 = [str stringByReplacingOccurrencesOfString:#" " withString:#""];
NSString *str1 = [str0 stringByReplacingOccurrencesOfString:#"(" withString:#""];
NSString *str2 = [str1 stringByReplacingOccurrencesOfString:#")" withString:#""];
NSString *str3 = [str2 stringByReplacingOccurrencesOfString:#"-" withString:#""];
return str3;
}
-(NSString*)getStringRepresentaionFromArray:(NSArray*)array {
return [array componentsJoinedByString:DELIMITER_SYMBOL];
}
Firstly, try some general approaches to reduce time by using more optimized code and less repetition of code and also through less use of loops or may be only iterate loops only till the data is obtained. Also you can check whether your code is properly distributed as far as the Time Profiling is concerned.
Secondly, we feel that time required is more because user is shown an Activity Indicator till 14 seconds. If you don't show it and don't block the User Interface till data is getting copied into your array, then user may feel that it is more smooth So here is how you can do that:
You can use NSThread to allow the application to launch while you retrieve all your data from the AddressBook and Store it in your array.
You can use
[NSThread detachNewThreadSelector:#selector(fetchAddressContacts:) withObject:nil];
For more information you can refer to detachNewThreadSelector: from NSThread
So basically in AppDelegate you need to code as following in AppDelegate.m
-(void)applicationDidFinishLaunching:(UIApplication *)application {
[NSThread detachNewThreadSelector:#selector(fetchAddressContacts) withObject:nil];
}
-(void)fetchAddressContacts {
//Do your stuff to copy your contacts from address book to array
// Once you finish this task you can trigger an NSNotification which could be caught on some other viewController or implement a delegate to transfer data to a viewController and proper actions can be executed once the data is loaded.
}
Let me know if you need more help.
Hope this helps you.
Perform your contacts retrieval method diagnostics and identify what calls are causing the bottleneck here.
Share your code and describe the bottlenecks
Any operation taking serious amount of time should be performed in a thread. In this case, I'd gather the data on first startup only and store them internally. You can refresh the primary data anytime later on demand (refresh button?) otherwise work with the internally stored "clone" of the contacts rather than calling any time-consuming operation on every app's start.
BTW: Be careful about the address book contacts these days :-)
When your app first install fetch all the contacts and save in coredate/plist file when next time app opens then show the contacts from the storage which is very fast as compare to fetching contacts from iPhone database. Now the problem how to update contacts which newly added, so for every contact there is property called "kABPersonModificationDateProperty" and "kABPersonCreationDateProperty" ,use this to find only updated contacts and add in the storage. Hope this helps.
Is possible to discover the iOS device identifier, using Xcode. I need to each app downloaded have a unique identifier. I thought in generate random numbers, but they might generate the same number more than once! Anyone have an idea?
In UIDevice class apple has provided method uniqueIdentifier but now its deprecated(for iOS5), In method's documentation you will find how you can use uniqueIdentifier.
I've found a pretty simple way to do this, here's how:
Press COMMAND + N and select Cocoa Touch Class.
Name your class NSString+UUID and hit next.
Then, replace the code in NSString+UUID.h with:
#interface NSString (UUID)
+ (NSString *)uuid;
#end
And in NSString+UUID.m with:
#implementation NSString (UUID)
+ (NSString *)uuid {
NSString *uuidString = nil;
CFUUIDRef uuid = CFUUIDCreate(NULL);
if (uuid) {
uuidString = (NSString *)CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
}
return [uuidString autorelease];
}
#end
Now, when you need to get the UUID (i.e: store it using NSUserDefaults when your app loads):
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
NSString *userIdentifierKey = #"user-identifier"
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:userIdentifierKey] == nil) {
NSString *theUUID = [NSString uuid];
[defaults setObject:theUUID forKey:userIdentifierKey];
[defaults synchronize];
}
// additional application setup...
return YES;
}
Try this UIDevice-with-UniqueIdentifier-for-iOS-5, it uses the device's mac address as unique identifier.
iOS6 provides a new replacement [[[UIDevice currentDevice] identifierForVendor] UUIDString]
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.
See: https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIDevice_Class/Reference/UIDevice.html#//apple_ref/doc/uid/TP40006902-CH3-SW49
You can create a category of UIApplication , UIDevice or as you prefere like this (ARC example)
#interface UIApplication (utilities)
- (NSString*)getUUID;
#end
#implementation UIApplication (utilities)
- (NSString*)getUUID {
NSUserDefaults *standardUserDefault = [NSUserDefaults standardUserDefaults];
static NSString *uuid = nil;
// try to get the NSUserDefault identifier if exist
if (uuid == nil) {
uuid = [standardUserDefault objectForKey:#"UniversalUniqueIdentifier"];
}
// if there is not NSUserDefault identifier generate one and store it
if (uuid == nil) {
uuid = UUID ();
[standardUserDefault setObject:uuid forKey:#"UniversalUniqueIdentifier"];
[standardUserDefault synchronize];
}
return uuid;
}
#end
UUID () is this function
NSString* UUID () {
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
return (__bridge NSString *)uuidStringRef;
}
this generate an unique identifier stored into the NSUserDefault to be reused whenever the application need it - This identifier will unique related to the application installs not to the device, but can be used for example to take trace about the number devices subscribed the APN service etc...
After that you can use it in this way:
NSString *uuid = [[UIApplication sharedApplication] getUUID];
try this
- (NSString *)getDeviceID
{
NSString *uuid = [self gettingString:#"uniqueAppId"];
if(uuid==nil || [uuid isEqualToString:#""])
{
CFUUIDRef theUUID = CFUUIDCreate(kCFAllocatorDefault);
if (theUUID)
{
uuid = NSMakeCollectable(CFUUIDCreateString(kCFAllocatorDefault, theUUID));
[self savingString:#"uniqueAppId" data:uuid];
[uuid autorelease];
CFRelease(theUUID);
}
}
return uuid;
// this is depreciated
// UIDevice *device = [UIDevice currentDevice];
// return [device uniqueIdentifier];
}
Use this: [[UIDevice currentDevice] identifierForVendor]
For more information take a look to this answer
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);