This question already has answers here:
UIDevice uniqueIdentifier deprecated - What to do now?
(32 answers)
Closed 5 years ago.
I am creating an ios app, since the udid is deprecated How can we uniquely identify the app.
You can use both UDID & UUID
For UUID- let uuid = NSUUID().uuidString
For UDID- let Identifier = UIDevice.current.identifierForVendor?.uuidString
Below Code Generate Unique ID for device and IT IS UNIQUE upto you will RESET the device. Happy coding...:)
- (NSString *)createNewUUID {
NSString *UUID = [[NSUUID UUID] UUIDString];
return UUID;
}
//Check And Retrive UniqueIdentifier
-(void)checkAndGenerateUniqueIdentifier{
//Retrive UDID
NSString *retrieveuuid = [SSKeychain passwordForService:[[NSBundle mainBundle] bundleIdentifier] account:[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey]];
if (retrieveuuid == nil) {
NSString *uuid = [self createNewUUID];
NSLog(#" Create new uuid : %#",uuid);
//Store the password in Keychain
NSError *error = nil;
[SSKeychain setPassword:uuid forService:[[NSBundle mainBundle] bundleIdentifier] account:[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey] error:&error];
if ([error code] == SSKeychainErrorNotFound) {
NSLog(#"Password not found");
}
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Check And Retrive UniqueIdentifier
[self checkAndGenerateUniqueIdentifier];
return YES;
}
Use Third party Keychain wrapper or iOS Keychain API for Store UUID.
Related
This question already has answers here:
iOS unique user identifier [duplicate]
(7 answers)
Closed 5 years ago.
Is there a way to identify a device even after having uninstalled an app and reinstalling again? I found topics where it's possible to get a UUID but it seems that after uninstalling the app the value of the UUID changes
The value in this property remains the same while the app (or another
app from the same vendor) is installed on the iOS device. The value
changes when the user deletes all of that vendor’s apps from the
device and subsequently reinstalls one or more of them.
I installed an App called Jodel, you don't have to create an Account to use the app and After uninstalling it, delete iCloud data, logging out from iCloud... an reinstalling it I was still logged in in the App. I assume they use a unique device identifier? Do you have an idea how such mechanism could be implemented?
You can use Keychain Service to store data still after uninstalling app from device.
for more reference about keychain service check this
https://developer.apple.com/documentation/security/keychain_services
Yes, It's Possible
#import "UICKeyChainStore.h"
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self getDeviceIdFromKeychain];
}
- (void)getDeviceIdFromKeychain
{
NSString *deviceID = [UICKeyChainStore stringForKey:#"KEY TO SAVE TO Keychain" service:nil];
// if vandorid from keychain is not nil
if (deviceID)
{
[[NSUserDefaults standardUserDefaults] setObject:deviceID forKey:#"deviceID"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
// else it goes for new vendorid and then stored it to keychan
else if (deviceID == (id)[NSNull null] || deviceID.length == 0 )
{
deviceID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[UICKeyChainStore setString:deviceID forKey:#"KEY TO SAVE TO Keychain" service:nil];
[[NSUserDefaults standardUserDefaults] setObject:deviceID forKey:#"deviceID"];
[[NSUserDefaults standardUserDefaults] synchronize];
// NSLog(#"VendorID Local - %#",deviceID);
}
}
ViewContoller.m
- (void)viewDidLoad
{
NSString *strDeviceId = [[NSUserDefaults standardUserDefaults]objectForKey:#"deviceID"];
}
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?
Can worklight return the device token for Android/iPhone/BB and if so how?
More specifically, I'm not looking for the "device id" but the native device token.
Worklight can return the "device id", but this is different than the device token. For example Worklight: How to get current device ID for Push subscription states how to get the "device id" using the call
WL.Client.getUserInfo("wl_deviceNoProvisioningRealm", "userId");
Unfortunately this returns something different than the device token. When using the native iPhone call like so and comparing it to the WL deviceid it's obvious they are different.
- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSMutableDictionary *results = [NSMutableDictionary dictionary];
NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
[results setValue:token forKey:#"deviceToken"];
#if !TARGET_IPHONE_SIMULATOR
[results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleDisplayName"] forKey:#"appName"];
[results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"] forKey:#"appVersion"];
NSUInteger rntypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
// Set the defaults to disabled unless we find otherwise...
NSString *pushBadge = #"disabled";
NSString *pushAlert = #"disabled";
NSString *pushSound = #"disabled";
if(rntypes & UIRemoteNotificationTypeBadge){
pushBadge = #"enabled";
}
if(rntypes & UIRemoteNotificationTypeAlert) {
pushAlert = #"enabled";
}
if(rntypes & UIRemoteNotificationTypeSound) {
pushSound = #"enabled";
}
[results setValue:pushBadge forKey:#"pushBadge"];
[results setValue:pushAlert forKey:#"pushAlert"];
[results setValue:pushSound forKey:#"pushSound"];
// Get the users Device Model, Display Name, Token & Version Number
UIDevice *dev = [UIDevice currentDevice];
[results setValue:dev.name forKey:#"deviceName"];
[results setValue:dev.model forKey:#"deviceModel"];
[results setValue:dev.systemVersion forKey:#"deviceSystemVersion"];
[self successWithMessage:[NSString stringWithFormat:#"%#", token]];
#else
[self successWithMessage:[NSString stringWithFormat:#"%#", #"simulator generated"]];
#endif
}
Moreover, the native device token is needed for a third party notification platform which is outside of worklight and so using worklights messaging system isn't feasible.
You're correct APNs device token and Worklight deviceId is two different things. In case you require APNs device token for using some 3rd party notification platform you can override the didRegisterForRemoteNotificationsWithDeviceToken method in your application delegate thus receiving full control over the device token once it arrives from APNs
I have this peace of code to get device id :
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSString *devicetokenRN = [NSString stringWithFormat:#"%#", deviceToken];
devicetokenRN = [devicetokenRN stringByReplacingOccurrencesOfString:#" " withString:#""];
devicetokenRN = [devicetokenRN stringByReplacingOccurrencesOfString:#"<" withString:#""];
devicetokenRN = [devicetokenRN stringByReplacingOccurrencesOfString:#">" withString:#""];
// send device token for APNS without spaces or <>
DeviceID *DeviceIdentifier = [[DeviceID alloc ] init];
// sending it to the Device.h
[DeviceIdentifier setDeviceID:devicetokenRN];
}
I need to retrieve this DeviceID as the first thing on my ViewController. As it doesn't do this function before it runs the view controller, because if I use NSlog in the ViewController it is NULL as it hasn't run this first. I need to get some variable like this first before it runs the UITableView. Any ideas would be brilliant thanks.
In AppDelegate, you can save the deviceToken value in NSUserDefaults like
put these line in didRegisterForRemoteNotificationsWithDeviceToken method in appdelegate.m like this
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:#"DeviceToken"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
also put this line of code in your view Controller.m and In ViewDidLoad method
and Retrieve that value.
[[NSUserDefaults standardUserDefaults] objectForKey:#"DeviceToken"];
You can get and store your deviceId in NSUserDefaults
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSUUID *identifierForVendor = [[UIDevice currentDevice] identifierForVendor];
NSString *deviceId = [identifierForVendor UUIDString];
NSUserDefaults *userRegistrarionDetails = [NSUserDefaults standardUserDefaults];
[userRegistrarionDetails setObject:deviceId forKey:#"deviceID"];
[userRegistrarionDetails synchronize];
}
And you can get anywhere in your whole project by
NSUserDefaults *userRegistrarionDetails = [NSUserDefaults standardUserDefaults];
NSString * deviceId = [userRegistrarionDetails objectForKey:#"deviceID"];
NSLog(#"%#", deviceId);
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.