NSUserDefaults - Cómo saber si existe una clave

208

Estoy trabajando en una pequeña aplicación para iPhone, y la estoy usando NSUserDefaultscomo mi persistencia de datos. Solo tiene que hacer un seguimiento de algunas cosas, como algunos nombres y algunos números, por lo que creo que también podría ser simple.

Encontré esta página como referencia, pero no creo que pueda responder a mi pregunta. Básicamente, quiero poder verificar si un valor (o una clave) ya existe en el NSUserDefaultsy luego hacer algo en consecuencia.

Algunos ejemplos: la aplicación se inicia, si es la primera vez que se inicia, emite una alerta que dice bienvenida. Para saber si es la primera vez que se abre, lee UserDefaultsy comprueba.

Ejemplo 2: Dice "Hola [Nombre]", donde Nombre es algo que ha ingresado. Si ha abierto la aplicación y no hay nombre, debería decir "Hola Mundo". Necesito verificar si ya ha ingresado un nombre y actuar en consecuencia. El nombre se almacenaría enNSUserDefaults .

¿Alguna ayuda aquí? Realmente lo apreciaría!

Ethan Mick
fuente

Respuestas:

382

objectForKey:volverá nilsi no existe.

jspcal
fuente
1
No creo que pueda almacenar un tipo de datos primitivo en NSUserDefaults.
kender
12
Los documentos de Apple dicen que "si un valor booleano está asociado con defaultName en los valores predeterminados del usuario, ese valor se devuelve. De lo contrario, NO se devuelve". No creo que la respuesta anterior sea correcta para BOOL, no puede determinar si se define NO o no existe. Creo que tendrías que usar – dictionaryRepresentationy verificar la clave.
zekel
40
@zekel En lugar de adivinar, probé esto (en iOS 5.1.1), y definitivamente detectó si un BOOL estaba presente o no, independientemente de cuál sea el valor de dicho BOOL. "objectForKey" devolvió nil cuando BOOL no estaba presente porque nunca se había configurado.
DataGraham
8
Si tiene un BOOL y lo prueba con boolForKey, entonces @zekel es correcto, obtiene SÍ o NO. Si lo prueba con objectForKey (como sugiere la respuesta) obtendrá cero si la clave no está configurada.
Giuseppe Garassino
2
Esto ya no funciona al menos a partir de iOS 6.1 Simulator. objectForKey devuelve el mismo valor si no está presente y si está presente con un BOOL de NO. La solución de i.jameelkhan funciona
lschult2
98

Como se mencionó anteriormente, no funcionará para tipos primitivos donde 0 / NO podría ser un valor válido. Estoy usando este código.

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

    NSLog(@"mykey found");
}
i.jameelkhan
fuente
Esto me salvó. ¡Gracias!
BalestraPatrick
Esta es la respuesta correcta cuando se trata de primitivas como BOOL. Distinguirá con precisión entre NOy no establecido, a diferencia objectForKey:.
devios1
@ devios1: si falta la clave, objectForKey:volverá nilindependientemente de la intención del programador de almacenar eventualmente uno Boolo cualquier otro tipo de datos. Cuando hay una primitiva presente, objectForKey:no regresa nilincluso si la clave está asociada con un valor primitivo.
Ted Hopp
Esta es la respuesta correcta: obviamente, la respuesta aceptada es incorrecta ya que objectForKey confunde 0 con nil, por lo que no puede funcionar. Probado con éxito desde iOS 4.3 a 10.2.1
Chrysotribax
Sé que esto es antiguo, pero solo ahora que necesitaba esta información, necesito señalar que la referencia 'contieneObjeto:' significa solo eso: el objeto. NO la clave. IOW, en su archivo de encabezado si ha definido: #define kMyKey @ "myKey", el 'contieneObjeto' no está buscando 'kMyKey', está buscando 'myKey'. El uso de 'kMyKey' siempre devolverá 'NO'.
Bill Norman
55

El objectForKey:método devolverá nilsi el valor no existe. Aquí hay una simple prueba IF / THEN que le dirá si el valor es nulo:

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

" objectForKey devolverá nil si no existe. " También devolverá nil si existe y es un entero o un booleano con un valor de cero (es decir, FALSO o NO para el booleano).

He probado esto en el simulador para 5.1 y 6.1. Esto significa que no se puede probar si se han establecido enteros o booleanos preguntando "el objeto". Puede salirse con la suya para enteros si no le importa tratar "no establecido" como si fuera "establecido a cero".

Las personas que ya probaron esto parecen haber sido engañadas por el aspecto falso negativo, es decir, probar esto al ver si objectForKey devuelve nil cuando se sabe que la clave no se ha configurado, pero no se da cuenta de que también devuelve nil si la clave ha sido establecido pero se ha establecido en NO.

Para mi propio problema, que me envió aquí, terminé cambiando la semántica de mi booleano para que mi valor predeterminado deseado estuviera en congruencia con el valor establecido en NO. Si esa no es una opción, deberá almacenar como algo distinto de un valor booleano y asegurarse de que puede distinguir entre SÍ, NO y "no establecido".

JamesKVL
fuente
He confirmado esto, pero hay una solución fácil; solo use los nuevos literales de objeto o una expresión en recuadro. @0en lugar de 0, en @NOlugar de NOo simplemente @(variable). Lee sobre ellos aquí.
kaka
1
Un poco tarde, pero en beneficio de los novatos: esto es incorrecto. object (forKey) en los valores de UserDefault de enteros establecidos en 0, y Bools establecido en false, devolverá correctamente un valor nulo. Si usa bool (forKey) para probar si un valor está configurado, puede encontrarse con problemas (porque si el valor está establecido en False, bool (forKey) devolverá 'falso', aunque espere 'verdadero')
thecloud_of_unknowing
5

Swift 3/4:

Aquí hay una extensión simple para los tipos de valor clave Int / Double / Float / Bool que imitan el comportamiento de retorno opcional de los otros tipos a los que se accede a través de UserDefaults.

( Editar 30 de agosto de 2018: actualizado con una sintaxis más eficiente a partir de la sugerencia 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
    }
}

Ahora son más consistentes junto con los otros métodos de obtención incorporados (cadena, datos, etc.). Simplemente use los métodos get en lugar de los antiguos.

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
stef
fuente
2
Preferiría return self.object(forKey: key) as? Intbuscar valor solo una vez.
Leo
3

Acabo de pasar por esto, y todo sus respuestas me ayudaron a encontrar una buena solución para mí. Me resistí a seguir la ruta sugerida, solo porque me resultaba difícil de leer y comprender.

Esto es lo que hice. Tenía un BOOL transportado en una variable llamada "_talkative".

Cuando configuré mi objeto predeterminado (NSUserDefaults), lo configuré como un objeto, ya que luego pude probar para ver si era nulo:

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

Luego, cuando fui a ver si existía, solía:

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

Luego usé el objeto como BOOL:

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

Esto parece funcionar en mi caso. Simplemente tenía más sentido visual para mí.

James Burns
fuente
Esto funcionó para mí ([predeterminado boolForKey: @ "
talkative
3

Prueba este pequeño bollo:

-(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];
    }
}

Trata todo como un objeto. Parece funcionar para mi.

pepelkod
fuente
3

Versión rápida para obtener Bool?

NSUserDefaults.standardUserDefaults().objectForKey(DefaultsIsGiver) as? Bool
Ben
fuente
1
¿Por qué no usar boolForKey? NSUserDefaults.standardUserDefaults().boolForKey(DefaultsIsGiver)
JAL
1
boolForKeyvolverá Booly no Bool?, así que si la clave no está allí, lo conseguirás falsey no lo harásnil
Ben
3

Extienda UserDefaultsuna vez para no copiar y pegar esta solución:

extension UserDefaults {

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

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

En Swift3, he usado de esta manera

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

La respuesta es excelente si vas a usar eso varias veces.

Espero que ayude :)

Nikhil Manapure
fuente
-1

Swift 3.0

if NSUserDefaults.standardUserDefaults().dictionaryRepresentation().contains({ $0.0 == "Your_Comparison_Key" }){
                    result = NSUserDefaults.standardUserDefaults().objectForKey(self.ticketDetail.ticket_id) as! String
                }
Kiran Jasvanee
fuente