NSUserDefaults - Comment savoir si une clé existe


208

Je travaille sur une petite application iPhone et j'utilise NSUserDefaultscomme persistance de données. Il n'a qu'à garder une trace de quelques choses, comme certains noms et certains chiffres, donc je pense que je pourrais aussi bien rester simple.

J'ai trouvé cette page pour référence, mais je ne pense pas qu'elle puisse répondre à ma question. Fondamentalement, je veux pouvoir vérifier si une valeur (ou une clé) existe déjà dans le NSUserDefaultspuis faire quelque chose en conséquence.

Quelques exemples: L'application démarre, si c'est la première fois qu'elle démarre, elle émet une alerte disant bienvenue. Pour savoir si c'est la première fois qu'il s'ouvre, il lit les UserDefaultsvérifications et.

Exemple 2: Il dit, "Bonjour [Nom]", où Nom est quelque chose que vous avez entré. Si vous avez ouvert l'application et qu'il n'y a pas de nom, elle devrait indiquer «Bonjour tout le monde». Je dois vérifier si vous avez déjà entré un nom et agir en conséquence. Le nom serait stocké dans NSUserDefaults.

Un peu d'aide ici? Je l'apprécierais vraiment!

Réponses:


382

objectForKey:reviendra nils'il n'existe pas.


1
Je ne pense pas que vous puissiez stocker un type de données primitif dans NSUserDefaults.
kender

12
Les documents d'Apple indiquent que "si une valeur booléenne est associée à defaultName dans les valeurs par défaut de l'utilisateur, cette valeur est renvoyée. Sinon, NON est renvoyé." Je ne pense pas que la réponse ci-dessus soit correcte pour les booléens, vous ne pouvez pas déterminer si elle est définie NON ou n'existe pas. Je pense que vous devez utiliser – dictionaryRepresentationet vérifier la clé.
zekel

40
@zekel Plutôt que de deviner, j'ai testé cela (sur iOS 5.1.1), et il a définitivement détecté la présence ou non d'un BOOL, quelle que soit la valeur dudit BOOL. "objectForKey" a retourné nil lorsque le BOOL n'était pas présent car il n'avait jamais été défini.
DataGraham

8
Si vous avez un BOOL et le testez avec boolForKey, alors @zekel a raison, vous obtenez OUI ou NON. Si vous le testez avec objectForKey (comme la réponse le suggère), vous obtenez nil si la clé n'est pas définie.
Giuseppe Garassino

2
Cela ne fonctionne plus au moins depuis le simulateur iOS 6.1. objectForKey renvoie la même valeur si elle n'est pas présente et si elle est présente avec un BOOL de NO. La solution de i.jameelkhan fonctionne
lschult2

98

Comme mentionné ci-dessus, cela ne fonctionnera pas pour les types primitifs où 0 / NO pourrait être une valeur valide. J'utilise ce code.

NSUserDefaults *defaults= [NSUserDefaults standardUserDefaults];
if([[[defaults dictionaryRepresentation] allKeys] containsObject:@"mykey"]){

    NSLog(@"mykey found");
}

Cela m'a sauvé. Merci!
BalestraPatrick

C'est la bonne réponse lorsqu'il s'agit de primitives comme BOOL. Il fera une distinction précise entre NOet non défini, contrairement à objectForKey:.
devios1

@ devios1 - Si la clé est manquante, objectForKey:elle reviendra nilquelle que soit l'intention du programmeur de stocker éventuellement un Boolou tout autre type de données. Lorsqu'une primitive est présente, objectForKey:ne revient pas nilmême si la clé est associée à une valeur primitive.
Ted Hopp, le

C'est la bonne réponse: évidemment, la réponse acceptée est fausse car objectForKey confond 0 avec nil, donc cela ne peut pas fonctionner. Testé avec succès d'iOS 4.3 à 10.2.1
Chrysotribax

Je sais que c'est vieux, mais comme je n'ai besoin que de ces informations, je dois souligner que la référence «containsObject:» signifie exactement cela: l'objet. PAS la clé. IOW, dans votre fichier d'en-tête si vous avez défini: #define kMyKey @ "myKey" the 'containsObject' ne cherche pas 'kMyKey', il cherche 'myKey'. L'utilisation de «kMyKey» renvoie toujours «NON».
Bill Norman

55

La objectForKey:méthode retournera nilsi la valeur n'existe pas. Voici un test IF / THEN simple qui vous dira si la valeur est nulle:

if([[NSUserDefaults standardUserDefaults] objectForKey:@"YOUR_KEY"] != nil) {
    ...
}

6

" objectForKey retournera nil s'il n'existe pas. " Il renverra également nil s'il existe et s'il s'agit soit d'un entier soit d'un booléen avec une valeur de zéro (c'est-à-dire FAUX ou NON pour le booléen).

J'ai testé cela dans le simulateur pour 5.1 et 6.1. Cela signifie que vous ne pouvez pas vraiment tester si des entiers ou des booléens ont été définis en demandant "l'objet". Vous pouvez vous en tirer avec les entiers si cela ne vous dérange pas de traiter "non défini" comme s'il était "mis à zéro".

Les personnes qui ont déjà testé cela semblent avoir été dupées par le faux aspect négatif, c'est-à-dire le tester en voyant si objectForKey renvoie nil lorsque vous savez que la clé n'a pas été définie, mais ne remarquant pas qu'il renvoie également nil si la clé a été mais a été réglé sur NO.

Pour mon propre problème, qui m'a envoyé ici, je viens de changer la sémantique de mon booléen afin que la valeur par défaut que je souhaite soit en accord avec la valeur définie sur NO. Si ce n'est pas une option, vous devrez stocker autre chose qu'un booléen et vous assurer que vous pouvez faire la différence entre OUI, NON et "non défini".


Je l'ai confirmé, mais il existe une solution simple; utilisez simplement les nouveaux littéraux d'objet ou une expression encadrée. @0au lieu de 0, @NOau lieu de NO, ou tout simplement @(variable). Lisez à leur sujet ici.
kaka

1
Un peu tard, mais au profit des débutants: c'est faux. object (forKey) sur les valeurs UserDefault d'entiers définis sur 0 et Bools définis sur false, renverra correctement non nul. Si vous utilisez bool (forKey) pour tester si une valeur est définie, vous pouvez rencontrer des problèmes (car si la valeur est définie sur False, bool (forKey) renverra 'false', même si vous vous attendez à 'true'.)
thecloud_of_unknowing

5

Swift 3/4:

Voici une extension simple pour les types de valeur-clé Int / Double / Float / Bool qui imitent le comportement de retour facultatif des autres types accessibles via UserDefaults.

( Modifier le 30 août 2018: mise à jour avec une syntaxe plus efficace d'après la suggestion de Leo.)

extension UserDefaults {
    /// Convenience method to wrap the built-in .integer(forKey:) method in an optional returning nil if the key doesn't exist.
    func integerOptional(forKey: String) -> Int? {
        return self.object(forKey: forKey) as? Int
    }
    /// Convenience method to wrap the built-in .double(forKey:) method in an optional returning nil if the key doesn't exist.
    func doubleOptional(forKey: String) -> Double? {
        return self.object(forKey: forKey) as? Double
    }
    /// Convenience method to wrap the built-in .float(forKey:) method in an optional returning nil if the key doesn't exist.
    func floatOptional(forKey: String) -> Float? {
        return self.object(forKey: forKey) as? Float
    }
    /// Convenience method to wrap the built-in .bool(forKey:) method in an optional returning nil if the key doesn't exist.
    func boolOptional(forKey: String) -> Bool? {
        return self.object(forKey: forKey) as? Bool
    }
}

Ils sont désormais plus cohérents aux côtés des autres méthodes get intégrées (chaîne, données, etc.). Utilisez simplement les méthodes get à la place des anciennes.

let AppDefaults = UserDefaults.standard

// assuming the key "Test" does not exist...

// old:
print(AppDefaults.integer(forKey: "Test")) // == 0
// new:
print(AppDefaults.integerOptional(forKey: "Test")) // == nil

2
Je préférerais return self.object(forKey: key) as? Intpour rechercher une valeur une seule fois.
Leo

3

Je viens de passer par là, et tout vos réponses m'ont aidé à trouver une bonne solution pour moi. J'ai résisté à suivre l'itinéraire suggéré par, juste parce que j'avais du mal à lire et à comprendre.

Voici ce que j'ai fait. J'avais un BOOL transporté dans une variable appelée "_talkative".

Lorsque j'ai défini mon objet par défaut (NSUserDefaults), je l'ai défini comme un objet, car je pouvais alors tester pour voir s'il était nul:

//converting BOOL to an object so we can check on nil
[defaults setObject:@(_talkative) forKey:@"talkative"];

Puis quand je suis allé voir si ça existait, j'ai utilisé:

if ([defaults objectForKey:@"talkative"]!=nil )
  {

Ensuite, j'ai utilisé l'objet comme BOOL:

if ([defaults boolForKey:@"talkative"]) {
 ...

Cela semble fonctionner dans mon cas. Cela me faisait juste plus de sens visuel.


Cela a fonctionné pour moi ([valeurs par défaut boolForKey: @ "bavard"]
Vineesh TP

3

Essayez ce petit crumpet:

-(void)saveUserSettings{
NSNumber*   value;

value = [NSNumber numberWithFloat:self.sensativity];
[[NSUserDefaults standardUserDefaults] setObject:value forKey:@"sensativity"];
}
-(void)loadUserSettings{
    NSNumber*   value;
    value = [[NSUserDefaults standardUserDefaults] objectForKey:@"sensativity"];
    if(value == nil){
        self.sensativity = 4.0;
    }else{
        self.sensativity = [value floatValue];
    }
}

Traitez tout comme un objet. Semble fonctionner pour moi.


3

Version rapide pour obtenir Bool?

NSUserDefaults.standardUserDefaults().objectForKey(DefaultsIsGiver) as? Bool

1
Pourquoi ne pas utiliser boolForKey? NSUserDefaults.standardUserDefaults().boolForKey(DefaultsIsGiver)
JAL

1
boolForKeyreviendra Boolet non Bool?, donc si la clé n'est pas là, vous obtiendrez falseet nonnil
Ben

3

Étendez UserDefaultsune fois pour ne pas copier-coller cette solution:

extension UserDefaults {

    func hasValue(forKey key: String) -> Bool {
        return nil != object(forKey: key)
    }
}

// Example
UserDefaults.standard.hasValue(forKey: "username")

0

Dans Swift3, j'ai utilisé de cette façon

var hasAddedGeofencesAtleastOnce: Bool {
    get {
        return UserDefaults.standard.object(forKey: "hasAddedGeofencesAtleastOnce") != nil
    }
}

La réponse est excellente si vous devez l'utiliser plusieurs fois.

J'espère que ça aide :)


-1

Swift 3.0

if NSUserDefaults.standardUserDefaults().dictionaryRepresentation().contains({ $0.0 == "Your_Comparison_Key" }){
                    result = NSUserDefaults.standardUserDefaults().objectForKey(self.ticketDetail.ticket_id) as! String
                }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.