NSUserDefaults - Como saber se existe uma chave

208

Estou trabalhando em um pequeno aplicativo para iPhone e estou usando NSUserDefaultscomo persistência de dados. Ele só precisa acompanhar algumas coisas, como alguns nomes e alguns números, então eu acho que é melhor mantê-lo simples.

Encontrei esta página para referência, mas não acho que possa responder à minha pergunta. Basicamente, quero poder verificar se já existe um valor (ou uma chave) no NSUserDefaultse fazer algo de acordo.

Alguns exemplos: o aplicativo é iniciado, se for a primeira vez que é iniciado, ele gera um alerta dizendo "bem-vindo". Para saber se é a primeira vez que se abre, ele lê UserDefaultse verifica.

Exemplo 2: Ele diz "Olá [Nome]", onde Nome é algo que você digitou. Se você abriu o aplicativo e não há nome, ele deve dizer "Olá, mundo". Preciso verificar se você já digitou um nome e agir de acordo. O nome seria armazenado NSUserDefaults.

Alguma ajuda aqui? Eu realmente aprecio isso!

Ethan Mick
fonte

Respostas:

382

objectForKey:retornará nilse não existir.

jspcal
fonte
1
Eu não acho que você pode armazenar um tipo de dados primitivo nos NSUserDefaults.
quer
12
Os documentos da Apple dizem que "Se um valor booleano estiver associado ao defaultName nos padrões do usuário, esse valor será retornado. Caso contrário, NO será retornado". Não acho que a resposta acima seja correta para BOOLs, você não pode determinar se está definido NÃO ou se não existe. Eu acho que você teria que usar – dictionaryRepresentatione verificar a chave.
22412 zekel
40
@zekel Em vez de adivinhar, testei isso (no iOS 5.1.1) e ele definitivamente detectou se um BOOL estava presente ou não, independentemente do valor do referido BOOL. "objectForKey" retornou nulo quando o BOOL não estava presente porque nunca havia sido definido.
DataGraham 30/07/2012
8
Se você tiver um BOOL e testá-lo com boolForKey, @zekel está certo, você recebe SIM ou NÃO. Se você testá-lo com objectForKey (como a resposta sugere), será nulo se a chave não estiver definida.
Giuseppe Garassino
2
Isso não funciona mais pelo menos no iOS 6.1 Simulator. objectForKey retorna o mesmo valor se não estiver presente e se estiver presente com um BOOL de NO. A solução da i.jameelkhan funciona
lschult2
98

Como mencionado acima, não funcionará para tipos primitivos em que 0 / NO possa ser um valor válido. Eu estou usando esse código.

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

    NSLog(@"mykey found");
}
i.jameelkhan
fonte
Isso me salvou. Obrigado!
amigos estão
Esta é a resposta correta ao lidar com primitivos como BOOL. Diferenciará com precisão NOe não será definido, diferentemente objectForKey:.
devios1
@ devios1 - Se a chave estiver faltando, objectForKey:retornará nilindependentemente da intenção do programador de armazenar eventualmente um Boolou qualquer outro tipo de dados. Quando um primitivo está presente, objectForKey:ele não retorna, nilmesmo que a chave esteja associada a um valor primitivo.
Ted Hopp
Esta é a resposta certa: obviamente, a resposta aceita está errada, pois objectForKey confunde 0 com zero, portanto não pode funcionar. Testado com sucesso a partir do iOS 4.3 a 10.2.1
Chrysotribax
Eu sei que isso é antigo, mas, agora, apenas precisando dessas informações, preciso salientar que a referência 'containsObject:' significa exatamente isso: o objeto. NÃO é a chave. IOW, no arquivo de cabeçalho, se você definiu: #define kMyKey @ "myKey", o 'containsObject' não está procurando por 'kMyKey', está procurando por 'myKey'. Usar 'kMyKey' sempre retornará 'NÃO'.
Bill Norman
55

O objectForKey:método retornará nilse o valor não existir. Aqui está um teste IF / THEN simples que informa se o valor é nulo:

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

" objectForKey retornará nulo se não existir. " Também retornará nulo se existir e é um número inteiro ou um booleano com valor zero (ou seja, FALSE ou NO para o booleano).

Eu testei isso no simulador para 5.1 e 6.1. Isso significa que você não pode realmente testar a existência de números inteiros ou booleanos pedindo "o objeto". Você pode se safar disso com números inteiros se não se importar em tratar "não definido" como se fosse "definido como zero".

As pessoas que já testaram isso parecem ter sido enganadas pelo aspecto falso negativo, ou seja, testando isso observando se objectForKey retorna nulo quando você sabe que a chave não foi definida, mas não percebeu que também retorna nulo se a chave foi definido, mas foi definido como NÃO.

Para o meu próprio problema, que me enviou aqui, acabei alterando a semântica do meu booleano para que meu padrão desejado estivesse em congruência com o valor definido como NÃO. Se isso não for uma opção, será necessário armazenar como algo diferente de um booleano e verifique se você sabe a diferença entre SIM, NÃO e "não definido".

JamesKVL
fonte
Eu confirmei isso, mas há uma solução fácil; basta usar os novos literais de objeto ou uma expressão em caixa. @0em vez de 0, em @NOvez de NO, ou simplesmente @(variable). Leia sobre eles aqui.
Kako1
1
Um pouco tarde, mas para o benefício de iniciantes: isso está incorreto. O objeto (forKey) nos valores UserDefault de números inteiros definidos como 0 e Bools definidos como falso retornará corretamente não nulo. Se você usar bool (forKey) para testar se um valor está definido, poderá encontrar problemas (porque se o valor estiver definido como False, bool (forKey) retornará 'false', mesmo que você esteja esperando 'true'.)
thecloud_of_unknowing
5

Swift 3/4:

Aqui está uma extensão simples para os tipos de valores-chave Int / Double / Float / Bool que imitam o comportamento de retorno opcional dos outros tipos acessados ​​por UserDefaults.

( Editar em 30 de agosto de 2018: atualizado com uma sintaxe mais eficiente da sugestão 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
    }
}

Agora eles são mais consistentes com os outros métodos get internos (string, dados, etc.). Basta usar os métodos get no lugar dos métodos antigos.

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
fonte
2
Eu preferiria return self.object(forKey: key) as? Intpesquisar valores apenas uma vez.
21316 Leo
3

Acabei de passar por isso, e todas as suas respostas me ajudaram a encontrar uma boa solução para mim. Resisti a seguir o caminho sugerido por, apenas porque achava difícil ler e compreender.

Aqui está o que eu fiz. Eu tinha um BOOL sendo carregado em uma variável chamada "_talkative".

Quando defino meu objeto padrão (NSUserDefaults), defino-o como um objeto, pois então eu poderia testar para ver se era nulo:

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

Então, quando fui ver se existia, usei:

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

Então eu usei o objeto como um BOOL:

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

Isso parece funcionar no meu caso. Isso só fez mais sentido visual para mim.

James Burns
fonte
Isso funcionou para mim ([padrão: boolForKey: @ "talkative"])
Vineesh TP
3

Experimente este pequeno bolinho:

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

Trate tudo como um objeto. Parece funcionar para mim.

pepelkod
fonte
3

Versão rápida para obter Bool?

NSUserDefaults.standardUserDefaults().objectForKey(DefaultsIsGiver) as? Bool
Ben
fonte
1
Por que não usar boolForKey? NSUserDefaults.standardUserDefaults().boolForKey(DefaultsIsGiver)
JAL
1
boolForKeyretornará Boole não Bool?, portanto, se a chave não estiver lá, você receberá falsee não #nil
Ben
3

Estenda UserDefaultsuma vez para não copiar e colar esta solução:

extension UserDefaults {

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

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

No Swift3, eu usei dessa maneira

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

A resposta é ótima se você a usar várias vezes.

Espero que ajude :)

Nikhil Manapure
fonte
-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
fonte