I am developing an application with external databases. For this motive, I have to save an uuid the only identifier for every user who registers. This way, it will differ if the user is a new user or is a user already registered in the application.
I create a new uuid with this:
NSUUID *uuid = [NSUUID UUID];
And i save that at external databases with the username. How do I obtain this information when the user enter again to the application? Have I to save it in some place of the device?
For one, you could could use NSUserDefaults
To cache uuid:
[[NSUserDefaults standardUserDefaults] setObject:uuid forKey:#"userIDKey"];
To retrieve uuid:
NSString *uuid = [[NSUserDefaults standardUserDefaults] objectForKey:#"userIDKey"];
Note that using the NSUUID class restricts you to an iOS target of iOS 6.0+. There are other ways to generate a UUID < iOS 6.0, e.g.
+ (NSString*)stringWithUUID {
CFUUIDRef uuidObj = CFUUIDCreate(nil);
NSString *uuidString = (__bridge_transfer NSString*)CFUUIDCreateString(nil, uuidObj);
CFRelease(uuidObj);
return uuidString;
}
Related
I wanted to know that How to generate Unique ID of device for iPhone/iPad using Objective-c
so that once application is installed on device , so we should track that deviceID
I have searched to retrieve IMEI of iPhone/iPad , but it is not allowed in objective-c.
Then I searched to generate UDID of iPhone/iPad but it is generating for different ID each time I launched it on simulator .
Yes, UDID is deprecated; we are not allowed to get UDID due to user privacy purposes. Apple does not allow to get any identifiers that uniquely identifies a device, such as IMEI, MAC address, UDID etc.
UUID is the best way to go as of now. But that would be unique for each vendor. You are not assured that it will be unique each time you get the UUID string. Best bet is to store the UUID string to phone's Keychain and retrieve it when needed, with a catch. When you factory-reset your phone, the keychain items would be erased. This limitation should be kept in mind.
UPDATE - IN IOS 10.3 BETA'S:
It seems that Apple has made some changes to how Keychain works in iOS 10.3+. Keychain items stored in the Keychain will be deleted when the all the apps from the specific vendor are uninstalled. According to Apple, the residence of sensitive information of an app even after the app is gone from the device may lead to security risks, so they decided to forbid this kind of behavior.
Developers relying on Keychain storage even after an uninstall for their apps can make use of this WORKAROUND to continue with the intended functionality. According to this workaround, any app can access the information stored in that specific Keychain Access Group, so it is recommended that adding an extra layer of encryption to your data will protect it with even more security, although keychain encrypts items by default.
UPDATE - IOS 10.3.3 (STABLE):
It seems that the keychain items deletion was a BUG in early betas of iOS 10.3.3 and was fixed later in the stable release. This might have been caused during betas since strange things can happen during that phase. It should be no problem to use Keychain hereafter.
You can use UUID (Universal User Identification). Following link contains apple documentation
https://developer.apple.com/reference/uikit/uidevice/1620059-identifierforvendor
https://developer.apple.com/library/content/releasenotes/General/WhatsNewIniOS/Articles/iOS7.html#//apple_ref/doc/uid/TP40013162-SW1
you can use this code for UUID:
//Objective-C
NSString * string = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
//Swift
let deviceID = UIDevice.currentDevice().identifierForVendor?.UUIDString
//Swift 3
let deviceID = UIDevice.current.identifierForVendor?.uuidString
Use the below code to get UDID for iOS device
Use KeychainItemWrapper Class download from URL
KeychainItemWrap
NSString *uuid;
KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"AC_APP_UUID" accessGroup:nil];
NSString *keychainUUID = [keychain objectForKey:(__bridge id)(kSecAttrAccount)];
NSString *appVersion = [NSString stringWithFormat:#"%#",#"1.0"];
[keychain setObject:appVersion forKey:(__bridge id)(kSecAttrDescription)];
if (keychainUUID==nil||[keychainUUID isKindOfClass:[NSNull class]]||keychainUUID.length==0) {
uuid = [[NSUUID UUID] UUIDString];
[keychain setObject:uuid forKey:(__bridge id)(kSecAttrAccount)];
}else{
uuid = [keychain objectForKey:(__bridge id)(kSecAttrAccount)];
}
Anyone coming here post 2017, Apple has implemented DeviceCheck for this purposes.
https://developer.apple.com/documentation/devicecheck#overview
You can use DeviceCheck to see if this particular device has installed or used your particular app. It doesn't give you a unique id to the phone, but it does allow you to see if a user has burned through a promotion or not.
You cannot take IMEI and phone number of user mobile, Apple is restricted to get these uniqueID's.
You have to store UDID in keychain. for this you have to download keychainwrapper class and store the UDID generated by above code:
UIDevice *device = [[UIDevice alloc]init];
NSString *idForVend = [NSString stringWithFormat:#"%#", [device identifierForVendor]];
NSLog(#"Identifier For Vendor : %#",idForVend);
follow this link it will solve your problem for sure: https://stackoverflow.com/questions/16459879/how-to-store-a-string-in-keychain-ios , iOS?
If you use this If you delete app And install again it will remain same DeviceID.
#import <AdSupport/AdSupport.h>
NSString* sClientID = [[[ASIdentifierManager sharedManager]advertisingIdentifier] UUIDString];
The Best UUID because:
it will never change (*even if the app will be deleted)
Apple approve it.
Solution is :
You can do it with DeviceToken . DeviceToken are uniq for all mobile.
Code is here :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[UIApplication sharedApplication] respondsToSelector:#selector(registerUserNotificationSettings:)])
{
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
else
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert)];
}
return yes;
}
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString *AppDeviceToken=[[NSString alloc] initWithFormat:#"%#",deviceToken];
//NSLog(#"My token is: %#", self.AppDeviceToken);
AppDeviceToken = [self.AppDeviceToken stringByReplacingOccurrencesOfString:#" " withString:#""];
AppDeviceToken = [self.AppDeviceToken stringByReplacingOccurrencesOfString:#"<" withString:#""];
AppDeviceToken = [self.AppDeviceToken stringByReplacingOccurrencesOfString:#">" withString:#""];
NSLog(#"%#'s Device Token is : %#",[[UIDevice currentDevice] name],AppDeviceToken);
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
NSLog(#"Failed to get token, error: %#", error);
}
Device token are uniq for all device.
I have to implement a UDID-like string for my application.
Therefore I used identifierForVendor to make a unique ID for my app and saved it to keychain with SSKeychain, in case it is changed each time the user reinstalls my application.
For each time I have to use the identifier, I will check in keychain whether if it's existed or I create and save one:
-(NSString *)getUniqueDeviceIdentifierAsString
{
NSString *strApplicationUUID = [SSKeychain passwordForService:self.appName account:#"myapp"];
if (strApplicationUUID == nil)
{
strApplicationUUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[SSKeychain setPassword:strApplicationUUID forService:self.appName account:#"myapp"];
}
return strApplicationUUID;
}
I'm just afraid that the ID will be synced across user's devices then it couldn't be "UDID-like" anymore.
I wonder if this is a good practice for my app?
According to apple docs, sometimes it happens that the method [ASIdentifierManager sharedManager].advertisingIdentifier can return a nil value and I really need this value in my app:
https://developer.apple.com/library/prerelease/ios/documentation/AdSupport/Reference/ASIdentifierManager_Ref/index.html#//apple_ref/occ/instp/ASIdentifierManager/advertisingIdentifier
If the value is nil, wait and get the value again later. This happens,
for example, after the device has been restarted but before the user
has unlocked the device.
How long should I wait? What approach should I use in this case? Maybe keep trying to get the advertisingIdentifier until it is available? Or should I tell the user to try to restart the app? Any ideas?
Thanks.
You should manually create UUID and should save it in preferences.
NSString *UUID = [[NSUserDefaults standardUserDefaults] objectForKey:kApplicationUUIDKey];
if (!UUID) {
CFUUIDRef uuid = CFUUIDCreate(NULL);
UUID = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
[[NSUserDefaults standardUserDefaults] setObject:UUID forKey:kApplicationUUIDKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
You can effectively get a notification when the user unlocks the phone using protected files and listening for applicationProtectedDataDidBecomeAvailable
Say I have an app - 'MyApp' - is there a way to consistently get a device identifier string within 'MyApp'?
i.e. Any time a user uses 'MyApp' on a particular device, I could call the API and get back the same string all the time?
identifierForVendor will change between installs, best to use CFUUIDCreate.
if ([[NSUserDefaults standardUserDefaults] valueForKey:#"UUID"] == nil) {
CFUUIDRef uuid = CFUUIDCreate(NULL);
NSString *uniqueIdentifier = ( NSString*)CFBridgingRelease(CFUUIDCreateString(NULL, uuid));
CFRelease(uuid);
[[NSUserDefaults standardUserDefaults] setObject:uniqueIdentifier forKey:#"UUID"];
}
The above will survive app re-installs.
Apple may reject your app for sending personally identifiable information. Use an advertising ID instead:
#import <AdSupport/AdSupport.h>
// Advertising ID
ASIdentifierManager *adManager = [ASIdentifierManager sharedManager];
NSUUID *uuid = [adManager advertisingIdentifier];
NSString *stringRepresentation = uuid.UUIDString;
Just make sure you abide by Apple's guidelines for UDID usage, including restrictions for users that have checked 'Restrict Ad Tracking' in Settings.
My published application makes use of the CFUUID and SSKeychain in order to identify the device (and to keep that ID unchanged even if the app is uninstalled and reinstalled)
I save those device ID in the server, and I recently noticed that some users have several of those IDs for the same real device. The only explanation I see is that the ID is not being saved or loaded from the Keychain and thus the device generates a new one. The strange thing is that it works fine on some other devices running the same iOS version.
Any ideas on what could be going on?
This is my related code in (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSString* identifier = #"appName";
NSString* serviceName = #"com.company.appName";
NSString *retrieveuuid = [SSKeychain passwordForService:serviceName account:identifier];
if (retrieveuuid == nil) {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
NSString *uuid = (NSString*) string;
[SSKeychain setPassword:uuid forService:serviceName account:identifier];
}
EDIT: My guess is that retrieveuuid == nil is not working as expected for some reason. Later in the app I register for push notifications and send the push token to the server together with this CFUUID that I read with the same exact line [SSKeychain passwordForService:serviceName account:identifier] but when it is sent to the server it is not nil (So I can see several CFUUIDs with the same push token).
EDIT 2 to attach more actual code.
AppDelegate.m
NSString* identifier = #"appName";
NSString* serviceName = #"com.company.appName";
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Creating UUID
NSString *retrieveuuid = [AppDelegate getDeviceId];
if (retrieveuuid == nil) {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
NSString *uuid = (NSString*) string;
[SSKeychain setPassword:uuid forService:serviceName account:identifier];
}
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
+ (NSString*) getDeviceId {
return [SSKeychain passwordForService:serviceName account:identifier];
}
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString *newToken = [deviceToken description];
newToken = [newToken stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
newToken = [newToken stringByReplacingOccurrencesOfString:#" " withString:#""];
_deviceToken = [[NSString alloc] initWithString:newToken];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *user = [prefs objectForKey:#"appNameUsername"];
if(user && ![user isEqualToString:#""]){
RestClient *rest = [[RestClient alloc] init];
rest.delegate = self;
rest.tag = 2;
[rest updateToken:newToken ForUsername:user];
[rest release];
}
}
RestClient.m
-(void) updateToken:(NSString *)token ForUsername:(NSString *)userName{
NSArray* info = [NSArray arrayWithObject: [NSMutableDictionary dictionaryWithObjectsAndKeys:
userName, #"Username",
token, #"TokenNo",
[[UIDevice currentDevice].model hasPrefix:#"iPad"] ? #"iPad" : #"iPhone", #"Device",
[UIDevice currentDevice].systemVersion, #"OSVersion",
[AppDelegate getDeviceId], #"DeviceID",
#"updateToken", #"CMD",
nil]];
[self doAction:info];
}
The doAction method just sends the data to the server and then callback the delegate, that part works fine. I can see on the server the logs of receiving this command:
"JSONCMD":"[
{ "TokenNo" : "f0d3aa21758350333b7e6315c38_EDIT_257c1838f49c43049f8380ec1ff63",
"AppVersion" : "1.0.4",
"Username" : "user#server.com",
"CMD" : "updateToken",
"OSVersion" : "7.0.4",
"DeviceID" : "9B794E11-6EF7-470C-B319-5A9FCCDAFD2B",
"Device" : "iPhone"
}
]
I see 2 possible candidates causing the strange behaviour, the NSStrings in the controller body and the static getDevice method. However, I don't see how this could work in many devices but fail in others.
I've had the same problem and I've found the solution. The Problem occurs with devices where user has the passcode set to unlock the phone. On iOS7 some code can run in the background (push notifications, background fetch) and if you save something in the keychain with standard accessibility type, you cannot read it when the device is locked (and the app is running in the background). Try to set this:
[SSKeychain setAccessibilityType:kSecAttrAccessibleAlways];
before you read/write the keychain.
Hope it helps.
NSString *retrieveuuid = [SSKeychain passwordForService:serviceName
account:identifier];
The retrieveuuid depends with the service, witch is always the same thing, but it depends also with the account. I think your problem is that one user can have multiple accounts in the same device, then your code are generating a UUID for each account.