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"];
}
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.
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
I have some code where I am reading a switch value from my settings bundle however on some instances it reads the wrong value.
This is what my code looks like, it's in my app delegate applicationDidBecomeActive
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
NSNumber *branchString = [settings objectForKey:#"signout"];
BOOL userSignOut = [[settings objectForKey:#"signout"] boolValue];
NSLog(userSignOut ? #"Yes" : #"No");
What happens is when the app first loads I receive NO then when I switch to settings and turn my switch over to YES, when I log back in I still get NO however if I then switch between settings and my app again with out touch the switch in settings that's when I get YES even though it should have happened a step earlier.
Are you using this when setting the value?
[[NSUserDefaults standardUserDefaults] setObject:(id) forKey:(NSString *)];
[[NSUserDefaults standardUserDefaults] synchronize];
You can use NSUserDefaultsDidChangeNotification as observer.
Apple documentation says we need to register for NSUbiquityIdentityDidChangeNotification and to compare the current iCloud token with the one previously stored in NSUserDefaults to detect if a user disabled iCloud from Documents & Data Settings or switched to another iCloud account.
I use a standard UIManagedDocument and I target iOS 7 so the fallback store is handled automatically by CoreData.
I do not understand what I should do after I find the user enabled / disabled iCloud or switched to another account. Should I migrate the persistent store? Or should I migrate it after an NSPersistentStoreCoordinatorStoresDidChangeNotification? Or should I never migrate it because everything is handled by CoreData?
After watching WWDC 2013 207 video several times I thought this would have been handled automatically by Core Data but I found that if I start with iCloud support and then I switch it off from Document & Data Settings and I insert new
data, then I switch back iCloud to on, I end with two different data sets.
I would like that if I find the user disabled iCloud then the local db should contain up to the last change made until the iCloud was enabled and only from this point everything should stop syncing until iCloud will be enabled again.
In the WWDC 2013 207 video, in the Melissa demo, I also noticed a call to a method [self migrateBack] after an NSPersistentStoreCoordinatorStoresDidChangeNotification and this confuses me because the slides just show we should save our context here and refresh the UI, they do not show we should migrate anything:
**Account Changes Now**
NSPersistentStoreCoordinatorStoresWillChangeNotification
[NSManagedObjectContext save:]
[NSManagedObjectContext reset:]
NSPersistentStoreCoordinatorStoresDidChangeNotification
[NSManagedObjectContext save:]
The NSPersistentStoreCoordinatorStoresDidChangeNotification notification has nothing to do with changing the iCloud access.
If the user turns off iCloud access or logs out of iCloud then Core Data has to use the fallback store, which is probably empty! You won't get a chance to migrate anything.
However if the app has its own Use iCloud setting then you can test for that change and if set to NO migrate any documents to local storage assuming iCloud is still available.
You can see the expected behaviour in the latest version of Pages. If you have iCloud documents then as soon as you turn off iCloud Documents & Data in the Settings App you loose all access to the Pages documents. However if you go the Pages settings in the Settings App and turn off Use iCloud and then switch back to Pages you will be prompted to Keep on My iPhone, Delete from My iPhone or Keep using iCloud. That is how Apple will expect your app to work.
When the application enters the foreground we check the app specific iCloud settings and if they have changed we take the necessary action.
If no change has been made then we keep running
If the iCloud setting has changed then:
If it is turned OFF ask the user what to do
If it has been turned ON then share all documents in iCloud
/*! The app is about to enter foreground so use this opportunity to check if the user has changed any
settings. They may have changed the iCloud account, logged into or out of iCloud, set Documents & Data to off (same effect as
if they logged out of iCloud) or they may have changed the app specific settings.
If the settings have been changed then check if iCloud is being turned off and ask the user if they want to save the files locally.
Otherwise just copy the files to iCloud (don't ask the user again, they've just turned iCloud on, so they obviously mean it!)
#param application The application
*/
- (void)applicationWillEnterForeground:(UIApplication *)application
{
//LOG(#"applicationWillEnterForeground called");
// Check if the app settings have been changed in the Settings Bundle (we use a Settings Bundle which
// shows settings in the Devices Settings app, along with all the other device settings).
[[NSUserDefaults standardUserDefaults] synchronize];
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
bool userICloudChoice = [userDefaults boolForKey:_cloudPreferenceKey];
// Now compare it with the current apps in memory setting to see if it has changed
if (userICloudChoice == useICloudStorage) {
// No change so do nothing
//LOG(#" iCloud choice has not changed");
} else {
// Setting has been changed so take action
//LOG(#" iCloud choice has been changed!!");
// iCloud option has been turned off
if (!userICloudChoice) {
//LOG(#" Ask user if they want to keep iCloud files locally ?");
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
_cloudChangedAlert = [[UIAlertView alloc] initWithTitle:#"You're not using iCloud" message:#"What would you like to do with documents currently on this phone?" delegate:self cancelButtonTitle:#"Keep using iCloud" otherButtonTitles:#"Keep on My iPhone", #"Delete from My iPhone", nil];
} else {
_cloudChangedAlert = [[UIAlertView alloc] initWithTitle:#"You're not using iCloud" message:#"What would you like to do with documents currently on this phone?" delegate:self cancelButtonTitle:#"Keep using iCloud" otherButtonTitles:#"Keep on My iPad", #"Delete from My iPad", nil];
}
[_cloudChangedAlert show];
// Handle the users response in the alert callback
} else {
// iCloud is turned on so just copy them across... including the one we may have open
//LOG(#" iCloud turned on so copy any created files across");
[[CloudManager sharedManager] setIsCloudEnabled:YES]; // This does all the work for us
useICloudStorage = YES;
}
}
}
- (void)alertView:(UIAlertView*)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView == _cloudChoiceAlert)
{
//LOG(#" _cloudChoiceAlert being processed");
if (buttonIndex == 1) {
//LOG(#" user selected iCloud files");
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:_cloudPreferenceKey];
[[NSUserDefaults standardUserDefaults] setValue:#"YES" forKey:_cloudPreferenceSet];
useICloudStorage = YES;
[[NSUserDefaults standardUserDefaults] synchronize];
[[CloudManager sharedManager] setIsCloudEnabled:YES];
}
else {
//LOG(#" user selected local files");
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:_cloudPreferenceKey];
[[NSUserDefaults standardUserDefaults] setValue:#"YES" forKey:_cloudPreferenceSet];
useICloudStorage = NO;
[[NSUserDefaults standardUserDefaults] synchronize];
[[CloudManager sharedManager] setIsCloudEnabled:NO];
}
}
if (alertView == _cloudChangedAlert)
{ //LOG(#" _cloudChangedAlert being processed");
if (buttonIndex == 0) {
//LOG(#" 'Keep using iCloud' selected");
//LOG(#" turn Use iCloud back ON");
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:_cloudPreferenceKey];
[[NSUserDefaults standardUserDefaults] synchronize];
useICloudStorage = YES;
}
else if (buttonIndex == 1) {
//LOG(#" 'Keep on My iPhone' selected");
//LOG(#" copy to local storage");
useICloudStorage = NO;
[[CloudManager sharedManager] setDeleteICloudFiles:NO];
[[CloudManager sharedManager] setIsCloudEnabled:NO];
}else if (buttonIndex == 2) {
//LOG(#" 'Delete from My iPhone' selected");
//LOG(#" delete copies from iPhone");
useICloudStorage = NO;
[[CloudManager sharedManager] setDeleteICloudFiles:YES];
[[CloudManager sharedManager] setIsCloudEnabled:NO];
}
}
}
/*! Checks to see whether the user has previously selected the iCloud storage option, and if so then check
whether the iCloud identity has changed (i.e. different iCloud account being used or logged out of iCloud).
If the user has previously chosen to use iCloud and we're still signed in, setup the CloudManager
with cloud storage enabled.
If no user choice is recorded, use a UIAlert to fetch the user's preference.
*/
- (void)checkUserICloudPreferenceAndSetupIfNecessary
{
FLOG(#"checkUserICloudPreferenceAndSetupIfNecessary called");
[[CloudManager sharedManager] setFileExtension:_fileExtension andUbiquityID:_ubiquityContainerKey ];
id currentToken = [[NSFileManager defaultManager] ubiquityIdentityToken];
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSString* userICloudChoiceSet = [userDefaults stringForKey:_cloudPreferenceSet];
bool userICloudChoice = [userDefaults boolForKey:_cloudPreferenceKey];
userICloudChoice = [userDefaults boolForKey:_cloudPreferenceKey];
//FLOG(#" User preference for %# is %#", _cloudPreferenceKey, (userICloudChoice ? #"YES" : #"NO"));
if (userICloudChoice) {
//LOG(#" User selected iCloud");
useICloudStorage = YES;
[self checkUbiquitousTokenFromPreviousLaunch:currentToken];
} else {
//LOG(#" User disabled iCloud");
useICloudStorage = NO;
}
// iCloud is active
if (currentToken) {
//LOG(#" iCloud is active");
// If user has not yet set preference the prompt for them to select a preference
if ([userICloudChoiceSet length] == 0) {
_cloudChoiceAlert = [[UIAlertView alloc] initWithTitle:#"Choose Storage Option" message:#"Should documents be stored in iCloud or on just this device?" delegate:self cancelButtonTitle:#"Local only" otherButtonTitles:#"iCloud", nil];
[_cloudChoiceAlert show];
}
else if (userICloudChoice ) {
[[CloudManager sharedManager] setIsCloudEnabled:YES];
}
}
else {
//LOG(#" iCloud is not active");
[[CloudManager sharedManager] setIsCloudEnabled:NO];
useICloudStorage = NO;
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:_cloudPreferenceKey];
[[NSUserDefaults standardUserDefaults] synchronize];
// Since the user is signed out of iCloud, reset the preference to not use iCloud, so if they sign in again we will prompt them to move data
[userDefaults removeObjectForKey:_cloudPreferenceSet];
}
[self storeCurrentUbiquityToken:currentToken];
}
I want to store an array with NSUserDefault, then, I put in applicationDidEnterBackground
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:#"myArray"];
and in application didFinishLaunchingWithOption
myArray= [[NSMutableArray alloc]
initWithArray:[[NSUserDefaults standardUserDefaults]
objectForKey:#"myArray"]];
it's ok for multitasking device, but for not-multitasking device, how can I solve?
Store the object in NSUserDefaults in -applicationWillTerminate:, if it hasn't already been saved by the invocation of -applicationDidEnterBackground: (i.e. check if multitasking is supported, if it is, then don't save it because it's already been saved.)
- (void) applicationWillTerminate:(UIApplication *) app {
if([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)] &&
![[UIDevice currentDevice] isMultitaskingSupported]) {
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:#"myArray"];
}
}
Do not forget to sync the buffer before going into background:
[[NSUserDefaults standardUserDefaults] synchronize];
The previous answers are all correct, but note that neither applicationDidEnterBackground nor applicationWillTerminate are guaranteed to be called in all situations. You are usually better off storing important data whenever it has changed.
Save NSUserDefaults at
- (void)applicationWillTerminate:(UIApplication *)application
set
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:#"myArray"];
in
applicationWillTerminate
and don't forget to use the encodeWithCoder and initWithCoder inside the object that you are trying to save and that is contained in the array