How to preserve identifierForVendor in ios after uninstalling ios app on device?
Asked Answered
A

8

73

I am developing an iOS app which calls web-service for login and at that time i send login credentials to web server along with vendor identifier (identifierForVendor),to identify device uniquely for those credentials.So user can have only one device and one credential.

I got identifierForVendor with

NSString *uuid = [[UIDevice currentDevice] identifierForVendor].UUIDString

This identifier will then store in database of web server and also in device database.Next time when user opens application and will try to download data from web server firstly local identifierForVendor on users device will compare with identifier stored on web server.

Problem occurs when user uninstall app and reinstall it, I found that identifierForVendor is changed. So user cannot proceed further.

I read apple documentation UIDevice Documentation

As mention there, if all app from same vendor uninstalls from device then at time of new installation of any app from that vendor will take new identifierForVendor.

So how to deal with this in my case ?

Airway answered 19/2, 2014 at 11:1 Comment(2)
I don't know if it's ok, but what about keeping it in Keychain? You check at launch if this identifier is in the KeyChain, and if not, your get one and store it in Keychain.Supernaturalism
Hi Gekb, did you found any solution for your query. Even I am also facing the same case.Headwork
S
66

You may keep it in KeyChain

-(NSString *)getUniqueDeviceIdentifierAsString
{

 NSString *appName=[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey];

 NSString *strApplicationUUID = [SSKeychain passwordForService:appName account:@"incoding"];
 if (strApplicationUUID == nil)
 {
    strApplicationUUID  = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    [SSKeychain setPassword:strApplicationUUID forService:appName account:@"incoding"];
 }

 return strApplicationUUID;
}
Snapdragon answered 19/2, 2014 at 11:6 Comment(15)
Will this work if iCloud KeyChain synchronisation is enabled?Johiah
Interesting question. Don't knowSnapdragon
This will not work when the KeyChain is synced, all synced device will get the same verdor id.Spectrometer
I'm just gonna vote this down because of the sync issue mentioned above.Ru
How about UIPasteBoard?Evanthe
@KristofVanLandschoot to make it works with iCloud you need to use a keychain within iCloud: https://mcmap.net/q/169867/-icloud-sync-keychainSystole
You can check my answer regarding SSKeychain and synchronization modeRogerrogerio
iOS11+ new API called DeviceCheck can uniquely identify a device, check answer: #24754037Stoops
Use updated version github.com/soffes/SAMKeychain of SSKeyChain.Tresa
Watch out for keychain though, items in the keychain currently survive an app uninstall-install cycle, but that may change in the future. In iOS 10.3 beta 3, keychain items was removed but that changed back in the final version. See more at #18911934 .Mylander
@NSIceCode it seems that the token returned by DeviceCheck API is ephemeral (is not stable) see the docs: developer.apple.com/documentation/devicecheck/dcdevice/…Bushido
I'm using getting the identifier for vender as nil sometimes only in iPhone 8 and iPhone X, can anyone suggest why I'm getting the nil instead of UDID? @NSIceCodeThreadfin
Keychain and UIPasteBoard are not persistent anymore : https://mcmap.net/q/169870/-iphone-keychain-items-persist-after-application-uninstallGwendagwendolen
swift 4 compile time error: Use of unresolved identifier 'SSKeychain'; did you mean 'SecKeychain'?Balancer
SSKeychain has been deprecated in favor of SAMKeychain (it is any third party library)Snapdragon
J
19

Generally, don't use identifierForVendor. Instead, use NSUUID to generate a custom UUID and store that in the keychain (because the keychain isn't deleted if the app is deleted and reinstalled).

January answered 19/2, 2014 at 11:5 Comment(7)
Is there a possibility for the identifierForVendor to return a duplicate in case for different versions of the same app on different devices?Primate
I would expect yes, but an infinitesimally small chanceJanuary
No chance of duplication. UUIDs are not random. They are calculated, and part of the UUID is the device ID.Hysteresis
"UUIDs created by NSUUID conform to RFC 4122 version 4 and are created with random bytes." - But none the less the chances of a repeat are very small, as someone else posted "Only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%." - https://mcmap.net/q/49698/-how-unique-is-uuidMarcelinomarcell
It is good to use the identifierForVendor, but this should not be store in the keychain anymore. https://mcmap.net/q/169870/-iphone-keychain-items-persist-after-application-uninstall It's not persistent since iOS 10.3Gwendagwendolen
@Gwendagwendolen you were correct, but now you're incorrect :)Poky
@MattMc Haha! so they made it persistent once more? XDGwendagwendolen
R
10

Addition to @nerowolfe's answer.

SSKeychain uses kSecAttrSynchronizableAny as a default synchronization mode. You probably don't want identifierForVendor to be synced across multiple devices so here is a code:

// save identifierForVendor in keychain without sync
NSError *error = nil;
SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
query.service = @"your_service";
query.account = @"your_account";
query.password = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
query.synchronizationMode = SSKeychainQuerySynchronizationModeNo;
[query save:&error];
Rogerrogerio answered 23/1, 2016 at 14:22 Comment(1)
From Cocoapods: SSKeychain has been deprecated in favor of SAMKeychainLemos
E
5

You can try use KeyChain to save your VendorIdentifier, that will exist till your device is reset, even if you uninstall your app.

Exemption answered 19/2, 2014 at 11:6 Comment(6)
Unfortunately, they changed it recently, the KeyChain is not safe to store persistent data anymore. https://mcmap.net/q/169870/-iphone-keychain-items-persist-after-application-uninstallGwendagwendolen
@Gwendagwendolen Thanks for update, my answer is 3 years old, will find another way, al the answers to the question still stores in Keychain they are not updated, its pointless to downvote, as it used to work when the question was asked..Exemption
Yeah I do understand pretty much all the answers are old. I was seeing the downvote more like a "this is not true anymore" as opposed to "this is a bad answer". But maybe I'm wrong.Gwendagwendolen
@Gwendagwendolen The answer still stands true see this https://mcmap.net/q/169869/-will-items-in-ios-keychain-survive-app-uninstall-and-reinstall and this https://mcmap.net/q/169871/-ios-10-3-beta-3-doesn-39-t-persist-data-of-keychainitemExemption
From your link : At Apple Documentation It is suggested that this is about to change and we should NOT rely on keychain access data being intact after an app uninstallationGwendagwendolen
@Gwendagwendolen This means my answer still works, for SHOULD NOT rely, YES of course I personally never use Keychain, but as far as it is working and useful for solving some problem we can use it, even if it is not the best solution, I deserve upvote as the answer is still working. Thanks.Exemption
C
4

Ok. I didn't want to use a third party - namely SSKeychain. So this is the code I tried, fairly simple and works well:

    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:bundleId accessGroup:nil];
if(![keychainItem objectForKey:(__bridge id)(kSecValueData)]){
    NSString *idfa = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    [keychainItem setObject:idfa forKey:(__bridge id)(kSecValueData)];
    NSLog(@"saving item %@", [keychainItem objectForKey:(__bridge id)(kSecValueData)]);
}else{
    NSLog(@"saved item is %@", [keychainItem objectForKey:(__bridge id)(kSecValueData)]);
}
Cabaret answered 14/1, 2015 at 14:6 Comment(5)
+1 This is a very simple solution, that combined with iCloud keychain sync (using the attribute kSecAttrSynchronizable), make the best solution at this question.Systole
@Systole if you use kSecAttrSynchronizable wouldn't you have the same value on all your devices?Antistrophe
@Antistrophe exactly, if you add kSecAttrSynchronizable to @gautam-jain code, you will get it on iCloud app properties and the synchronized to all your devices.Systole
then it's not a good idea if you want each device to have a different valueAntistrophe
@Antistrophe yes depending of your needs, you can preserve the device's identifier, the device's vendor of both like properties identifierForVendor and advertisingIdentifier from ASIdentifierManager.Systole
C
3

Swift version

func UUID() -> String {

    let bundleName = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
    let accountName = "incoding"

    var applicationUUID = SAMKeychain.passwordForService(bundleName, account: accountName)

    if applicationUUID == nil {

        applicationUUID = UIDevice.currentDevice().identifierForVendor!.UUIDString

        // Save applicationUUID in keychain without synchronization
        let query = SAMKeychainQuery()
        query.service = bundleName
        query.account = accountName
        query.password = applicationUUID
        query.synchronizationMode = SAMKeychainQuerySynchronizationMode.No

        do {
            try query.save()
        } catch let error as NSError {
            print("SAMKeychainQuery Exception: \(error)")
        }
    }

    return applicationUUID
}
Crack answered 3/8, 2016 at 13:58 Comment(1)
I wouldn't call this a Swift version as it requires a 3rd party objectLefty
S
2

There is no definite way to link a unique number to a device any more, this is not allowed with the Apple privacy guidelines.

You can try to save your own Unique ID in the keychain, but if the user clear his device this ID is also gone.

Generally is it just wrong to link a device to a user, since you are not longer identifying users but devices. So you should just change your API so that the user can re-login and that the vendor ID is bound to the users account.

Also what happens when the user has more then one device, like an iPhone and iPad, and uses you app on both? Since you authentication is based an unique ID this can not be done.

Spectrometer answered 19/2, 2014 at 11:8 Comment(3)
@DouglasHeld I understand that, but Apple made that impossible and I tried to explain why you should not.Spectrometer
@Spectrometer You need to identify a device if you require hardware binding. How else would you stop a user from installing it on multiple devices? One guy pays once and then all his friends get it for free as well using his login credentials!? Doesn't seem reasonable to the developers.Aspect
@ash I hear you, but Apple is not allowing this. Per Apple Account 5 devices are supported. Restricting this is against Apple policy.Spectrometer
I
1

I had used KeychainAccess pod for this problem.

In your pod file :

pod 'KeychainAccess', '~> 2.4' //If you are using Swift 2.3 
pod 'KeychainAccess' //Defaults to 3.0.1 which is in Swift 3

Import KeychainAccess module in file where you want to set UUID in keychain

import KeychainAccess

Use below code to set and get UUID from keychain :

Note : BundleId is key and UUID is value

var bundleID = NSBundle.mainBundle().bundleIdentifier
    var uuidValue = UIDevice.currentDevice().identifierForVendor!.UUIDString

 //MARK: - setVenderId and getVenderId
    func setVenderId() {

        let keychain = Keychain(service: bundleID!)

        do {
            try keychain.set(venderId as String, key: bundleID!)
            print("venderId set : key \(bundleID) and value: \(venderId)")
        }
        catch let error {
            print("Could not save data in Keychain : \(error)")
        }
    }

    func getVenderId() -> String {
        let keychain = Keychain(service: bundleID!)
        let token : String = try! keychain.get(bundleID!)!
        return token
    }
Intort answered 11/1, 2017 at 9:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.