93 votes

Identifiant unique de l'utilisateur iOS

J'écris une application pour iphone, qui communique avec mon serveur en utilisant REST. Le problème principal est que je dois identifier l'utilisateur d'une manière ou d'une autre. Il n'y a pas si longtemps, nous étions autorisés à utiliser l'UDID, mais maintenant ce n'est plus autorisé. Que dois-je donc utiliser à la place ? J'ai besoin d'une sorte d'identifiant sur l'iphone, de sorte que l'utilisateur supprime l'application, l'installe à nouveau et obtienne le même identifiant.

0 votes

Il s'agit d'un sujet très actuel et brûlant. Avez-vous d'abord cherché ici des questions similaires ? Et sur Google ? Sujet très actuel...

0 votes

Certains disent que nous devrions utiliser mac-address pour cette situation, mais je ne suis pas sûr que les règles d'Apple le permettent.

0 votes

Apple ne le permettra pas car il est aussi spécifique par appareil que l'est l'udid.

187voto

Moomio Points 1406

J'ai utilisé CFUUIDCreate() pour créer un UUID :

+ (NSString *)GetUUID {
  CFUUIDRef theUUID = CFUUIDCreate(NULL);
  CFStringRef string = CFUUIDCreateString(NULL, theUUID);
  CFRelease(theUUID);
  return [(NSString *)string autorelease];
}

Ensuite, définissez l'UUID ci-dessus à mon NSString :

NSString *UUID = [nameofclasswhereGetUUIDclassmethodresides UUID];

J'ai ensuite stocké cet UUID dans le trousseau de clés en utilisant SSKeyChain

Pour définir l'UUID avec SSKeyChain :

[SSKeychain setPassword:UUID forService:@"com.yourapp.yourcompany" account:@"user"];

Pour le récupérer :

NSString *retrieveuuid = [SSKeychain passwordForService:@"com.yourapp.yourcompany" account:@"user"];

Lorsque vous définissez l'UUID dans le trousseau, il persistera même si l'utilisateur désinstalle complètement l'application, puis l'installe à nouveau.

Pour s'assurer que TOUS les appareils ont le même UUID dans le trousseau.

  1. Configurez votre application pour utiliser iCloud.
  2. Enregistrez également l'UUID qui se trouve dans le trousseau de clés dans NSUserDefaults.
  3. Transmettez l'UUID dans NSUserDefaults au Cloud avec Key-Value Data Store.
  4. Lors de la première exécution de l'application, vérifiez si les données du nuage sont disponibles et définissez l'UUID dans le trousseau de clés sur le nouveau dispositif.

Vous disposez maintenant d'un identifiant unique qui est persistant et partagé/synchronisé avec tous les appareils.

1 votes

+1 C'est la meilleure approche que j'ai vue pour des uuid uniques par utilisateur et pas seulement pour les appareils ! merci beaucoup.

0 votes

Je ne comprends pas bien, pouvez-vous m'aider à mieux comprendre ? Quelqu'un serait-il prêt à discuter avec moi en temps réel pour m'aider à comprendre ? Je vous en serais très reconnaissant.

0 votes

Solution très agréable et facile. Elle fonctionne comme un charme. Merci

70voto

Ole Begemann Points 85798

Tout d'abord, l'UDID est seulement déprécié dans iOS 5. Cela ne signifie pas qu'il a disparu (pour le moment).

Deuxièmement, vous devez vous demander si vous avez vraiment besoin d'une telle chose. Que se passe-t-il si l'utilisateur obtient un nouvel appareil et installe votre application sur celui-ci ? Même utilisateur, mais l'UDID a changé. Entre-temps, l'utilisateur d'origine a peut-être vendu son ancien appareil, de sorte qu'un tout nouvel utilisateur installe votre application et vous pensez qu'il s'agit d'une personne différente sur la base de l'UDID.

Si vous n'avez pas besoin de l'UDID, utilisez CFUUIDCreate() pour créer un ID unique et le sauvegarder dans les valeurs par défaut de l'utilisateur lors du premier lancement (utiliser CFUUIDCreateString() pour convertir d'abord l'UUID en chaîne de caractères). Il survivra aux sauvegardes et aux restaurations et accompagnera même l'utilisateur initial lorsqu'il changera d'appareil. À bien des égards, c'est une meilleure option que l'UDID.

Si vous avez vraiment besoin d'une dispositif identifiant (vous n'avez pas l'air d'en avoir), cherchez l'adresse MAC comme indiqué dans la réponse de Suhail.

1 votes

CFUUIDCreate - si le même utilisateur, réinstallera mon application, restera-t-il le même, ou changera-t-il ?

0 votes

Ce sont des informations très utiles, merci de les avoir postées !

2 votes

@Drabuna : Non, CFUUIDCreate() créera un nouvel UUID à chaque fois que vous l'appellerez.

35voto

NSQuamber.java Points 2409

J'étais en train de mettre à jour mon application qui fonctionnait uniquement sur la base de l'identifiant unique qui supportait iOS 4.3 et supérieur. Donc,

1) Je n'ai pas pu utiliser [UIDevice currentDevice].uniqueIdentifier; car il n'était plus disponible

2) Je n'ai pas pu utiliser [UIDevice currentDevice].identifierForVendor.UUIDString car il n'était disponible qu'à partir de la version 6.0 d'iOS et ne pouvait être utilisé pour les versions inférieures d'iOS.

3) L'adresse mac n'était pas une option car elle n'était pas autorisée dans iOS-7

4) OpenUDID était déprécié il y a quelques temps et avait également des problèmes avec iOS-6.

5) Les identifiants de publicité n'étaient pas non plus disponibles pour iOS-5 et les versions inférieures.

Enfin, voici ce que j'ai fait

a) Ajouté SFHFKeychainUtils au projet

b) Chaîne de clés CFUUID générée

 CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
    udidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));

c) L'enregistrer dans Key Chain Utils sinon il générera un nouvel Unique à chaque fois.

Code final

+ (NSString *)GetDeviceID {
    NSString *udidString;
   udidString = [self objectForKey:@"deviceID"];
    if(!udidString)
    {
    CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
    udidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
    CFRelease(cfuuid);
        [self setObject:udidString forKey:@"deviceID"];
    }
    return udidString;
}

+(void) setObject:(NSString*) object forKey:(NSString*) key
{
    NSString *objectString = object;
    NSError *error = nil;
    [SFHFKeychainUtils storeUsername:key
                         andPassword:objectString
                      forServiceName:@"LIB"
                      updateExisting:YES
                               error:&error];

    if(error)
        NSLog(@"%@", [error localizedDescription]);
}

+(NSString*) objectForKey:(NSString*) key
{
    NSError *error = nil;
    NSString *object = [SFHFKeychainUtils getPasswordForUsername:key
                                                  andServiceName:@"LIB"
                                                           error:&error];
    if(error)
        NSLog(@"%@", [error localizedDescription]);

    return object;
}

enter image description here

Pour plus de détails

3 votes

Cette réponse mérite plus de votes positifs

2 votes

Bien expliqué. Cela devrait être la réponse acceptée

0 votes

@OrlandoLeite et user2043155. Merci. Je m'attendais à ce que cela soit considéré comme la bonne réponse. :'(

15voto

mikeho Points 310

Certaines personnes veulent en savoir plus sur les différentes options disponibles. Si c'est votre cas, jetez un coup d'œil à la réponse de @NSQuamber.java. Si vous voulez savoir comment utiliser le NSUUID et synchroniser avec iCloud, continuez à lire. Ce billet a fini par être plus long que je ne le souhaitais à l'origine, mais j'espère qu'il est clair pour tous ceux qui suivent ces étapes !

Utilisation de NSUUID

J'utilise la classe NSUUID pour créer l'UUID :

NSUUID *uuid = [NSUUID UUID];

Ensuite, pour créer la chaîne, il suffit d'appeler la fonction UUIDString méthode :

NSString *uuidString = [uuid UUIDString];

ou le faire en une seule ligne :

NSString *uuidString = [[NSUUID UUID] UUIDString];

IMHO, c'est beaucoup plus facile que d'essayer d'utiliser CFUUIDCreate et d'avoir une méthode que vous devez maintenir.


EDIT : J'utilise maintenant UICKeyChainStore

Pour définir l'UUID avec UICKeyChainStore :

UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.sample.MyApp"];
keychain[@"com.sample.MyApp.user"] = userID;

Pour le récupérer :

UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:@"com.sample.MyApp"];
NSString *userID = keychain[@"com.sample.MyApp.user"];

J'ai ensuite stocké cet UUID dans le trousseau de clés en utilisant SSKeyChain

Pour définir l'UUID avec SSKeyChain :

[SSKeychain setPassword:userID forService:@"com.sample.MyApp.user" account:@"com.sample.MyApp"];

Pour le récupérer :

NSString *userID = [SSKeychain passwordForService:@"com.sample.MyApp.user" account:@"com.sample.MyApp"];

Lorsque vous définissez l'UUID dans le trousseau, il persistera même si l'utilisateur désinstalle complètement l'application, puis l'installe à nouveau.

Synchronisation avec iCloud

Il est donc utile de s'assurer que tous les appareils de l'utilisateur utilisent le même UUID. Cela permet de s'assurer que les données sont synchronisées sur tous les appareils, plutôt que de laisser chaque appareil penser qu'il est un utilisateur unique.

Il y a eu plusieurs questions dans les commentaires pour ma réponse sur la façon dont la synchronisation fonctionnerait, donc maintenant que j'ai tout fait fonctionner, je vais fournir plus de détails.

Configuration de l'utilisation de iCloud/NSUbiquitousKeyValueStore

  1. Cliquez sur votre projet en haut de la page Navigateur de projet dans Xcode.
  2. Sélectionnez Capacités .
  3. Activez iCloud.

Il devrait maintenant ressembler à quelque chose comme ceci : Screenshot of iCloud enabled

Utilisation de NSUbiquitousKeyValueStore

L'utilisation d'iCloud est assez simple. Pour écrire :

// create the UUID
NSUUID *userUUID = [[NSUUID UUID];
// convert to string
NSString *userID = [userUUID UUIDString];
// create the key to store the ID
NSString *userKey = @"com.sample.MyApp.user";

// Save to iCloud
[[NSUbiquitousKeyValueStore defaultStore] setString:userID forKey:userKey];

A lire :

// create the key to store the ID
NSString *userKey = @"com.sample.MyApp.user";

// read from iCloud
NSString *userID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:userKey];

Avant de pouvoir écrire le Documentation sur le NSUbiquitousKeyValueStore indique que vous devez d'abord lire à partir d'iCloud. Pour forcer une lecture, appelez la méthode suivante :

[[NSUbiquitousKeyValueStore defaultStore] synchronize];

Pour que votre application reçoive des notifications de changements dans iCloud, ajoutez la notification suivante :

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(iCloudStoreDidChange:)
                                             name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
                                           object:[NSUbiquitousKeyValueStore defaultStore]];

Création de l'UUID avec iCloud

En combinant NSUUID, SSKeychain et NSUbiquityKeyValueStore, voici ma méthode pour générer un ID utilisateur :

- (NSUUID *)createUserID {
    NSString *userKey = @"com.sample.MyApp.user";
    NSString *KEYCHAIN_ACCOUNT_IDENTIFIER = @"com.sample.MyApp";
    NSString *userID = [SSKeychain passwordForService:userKey account:KEYCHAIN_ACCOUNT_IDENTIFIER];
    if (userID) {
        return [[NSUUID UUID] initWithUUIDString:userID];
    }

    // check iCloud
    userID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:userKey];
    if (!userID) {
        // none in iCloud, create one
        NSUUID *newUUID = [NSUUID UUID];
        userID = [newUUID UUIDString];
        // save to iCloud
        [[NSUbiquitousKeyValueStore defaultStore] setString:userID forKey:userKey];
    }

    // store the user ID locally
    [SSKeychain setPassword:userID forService:userKey account:KEYCHAIN_ACCOUNT_IDENTIFIER];
    return [[NSUUID UUID] initWithUUIDString:userID];
}

Comment s'assurer que votre ID utilisateur est synchronisé

Étant donné que l'écriture dans iCloud nécessite d'abord le téléchargement de toutes les données présentes dans iCloud, j'ai placé l'appel de synchronisation en haut de la page d'accueil de l'application. (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions méthode. J'y ai également ajouté l'enregistrement de la notification. Cela me permet de détecter tout changement provenant d'iCloud et de le traiter de manière appropriée.

En voici un exemple :

NSString *const USER_KEY = @"com.sample.MyApp.user";
NSString *const KEYCHAIN_ACCOUNT_IDENTIFIER = @"com.sample.MyApp";

- (void)iCloudStoreDidChange:(NSNotification *)notification {
    NSDictionary *userInfo = notification.userInfo;
    NSNumber *changeReason = userInfo[NSUbiquitousKeyValueStoreChangeReasonKey];
    NSArray *keysChanged = userInfo[NSUbiquitousKeyValueStoreChangedKeysKey];
    if (changeReason) {
        switch ([changeReason intValue]) {
            default:
            case NSUbiquitousKeyValueStoreServerChange:
            case NSUbiquitousKeyValueStoreInitialSyncChange:
                // check changed keys
                for (NSString *keyChanged in keysChanged) {
                    NSString *iCloudID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:keyChanged];
                    if (![keyChanged isEqualToString:USER_KEY]) {
                        NSLog(@"Unknown key changed [%@:%@]", keyChanged, iCloudID);
                        continue;
                    }

                    // get the local key
                    NSString *localID = [SSKeychain passwordForService:keyChanged account:KEYCHAIN_ACCOUNT_IDENTIFIER];
                    if (!iCloudID) {
                        // no value from iCloud
                        continue;
                    }
                    // local ID not created yet
                    if (!localID) {
                        // save the iCloud value locally
                        [SSKeychain setPassword:iCloudID forService:keyChanged account:KEYCHAIN_ACCOUNT_IDENTIFIER];
                        continue; // continue because there is no user information on the server, so no migration
                    }

                    if ([iCloudID isEqualToString:localID]) {
                        // IDs match, so continue
                        continue;
                    }

                    [self handleMigration:keyChanged from:localID to:iCloudID];
                }

                break;
            case NSUbiquitousKeyValueStoreAccountChange:
                // need to delete all data and download new data from server
                break;
        }
    }
}

Lorsque l'application est lancée ou lorsqu'elle revient au premier plan, je force une synchronisation avec iCloud et vérifie l'intégrité des UUID.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self configureSecKeyWrapper];
    // synchronize data from iCloud first. If the User ID already exists, then we can initialize with it
    [[NSUbiquitousKeyValueStore defaultStore] synchronize];
    [self checkUseriCloudSync];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // synchronize changes from iCloud
    [[NSUbiquitousKeyValueStore defaultStore] synchronize];
    [self checkUseriCloudSync];
}

- (BOOL)checkUseriCloudSync {
    NSString *userKey = @"com.sample.MyApp.user";
    NSString *KEYCHAIN_ACCOUNT_IDENTIFIER = @"com.sample.MyApp";
    NSString *localID = [SSKeychain passwordForService:userKey account:KEYCHAIN_ACCOUNT_IDENTIFIER];
    NSString *iCloudID = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:userKey];

    if (!iCloudID) {
        // iCloud does not have the key saved, so we write the key to iCloud
        [[NSUbiquitousKeyValueStore defaultStore] setString:localID forKey:userKey];
        return YES;
    }

    if (!localID || [iCloudID isEqualToString:localID]) {
        return YES;
    }

    // both IDs exist, so we keep the one from iCloud since the functionality requires synchronization
    // before setting, so that means that it was the earliest one
    [self handleMigration:userKey from:localID to:iCloudID];
    return NO;
}

Si l'UUID qui est venu en premier est important

Dans mon cas d'utilisation de mon UserID, j'ai supposé que la valeur dans iCloud est celle à conserver, puisqu'il s'agit du premier UUID envoyé à iCloud, quel que soit l'appareil qui a généré l'UUID en premier. La plupart d'entre vous suivront probablement le même chemin, puisque vous ne vous soucierez pas vraiment de l'UUID auquel il se résout, tant qu'il se résout à un seul. Pour ceux d'entre vous qui se soucient réellement de savoir lequel a été généré en premier, je vous suggère de stocker à la fois l'UUID et la génération de l'horodatage ( [[NSDate date] timeIntervalSince1970] ) afin que vous puissiez vérifier lequel est le plus ancien :

// using dates
NSDate *uuid1Timestamp = [NSDate dateWithTimeIntervalSince1970:timestamp1];
NSDate *uuid2Timestamp = [NSDate dateWithTimeIntervalSince1970:timestamp2];
NSTimeInterval timeDifference = [uuid1 timeIntervalSinceDate:uuid2Timestamp];

// or just subtract
double timeDifference = timestamp1 - timestamp2;

0 votes

Comment gérez-vous le cas où la synchronisation iCloud n'a pas encore eu lieu ? Par conséquent, vous auriez créé un nouvel UUID pour un appareil parce qu'il y a eu un retard dans la synchronisation ? Est-ce que vous exécutez ceci en production et avez-vous eu des problèmes concernant le retard de synchronisation ?

0 votes

@user1218464 : J'ai fait une grosse modification à ma réponse pour montrer comment je gère la synchronisation. Il n'y a pas de problème car j'effectue localement une migration des données vers le bon utilisateur et j'envoie également une requête à mon serveur pour qu'il fasse de même.

0 votes

Merci pour la mise à jour. C'est très utile. Je n'ai pas assez testé, mais avant que vous ne répondiez, j'ai essayé une approche légèrement différente consistant à synchroniser le trousseau de clés sur iCloud afin de ne pas avoir à le stocker dans NSUbiquitousKeyValueStore. Vous pouvez également obtenir un horodatage de la date de création d'un mot de passe de trousseau de clés pour vous assurer que vous utilisez toujours le plus ancien. Je ne suis pas sûr que ce soit la meilleure option, mais cela réduit définitivement le code.

10voto

Suhail Patel Points 9250

Il existe une alternative intéressante sur Github qui génère un identifiant unique basé sur une combinaison de l'adresse Mac et de l'identifiant de l'ensemble des données, qui fonctionne assez bien : UIDevice-with-UniqueIdentifier-for-iOS-5 (Périphérique avec identifiant unique pour iOS-5)

0 votes

Merci Patel Ji, c'est une meilleure option je suppose, car il peut y avoir des scénarios où nous voulons que l'application particulière émette toujours le même UIID pour ce dispositif.

1 votes

L'adresse MAC ne possède pas la propriété d'unicité nécessaire pour remplacer l'UDID. Voir ma réponse ici : stackoverflow.com/a/16230057/112191

9 votes

L'adresse MAC est désormais indisponible dans iOS 7.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X