Como salvar dados locais em um aplicativo Swift?

149

Atualmente, estou trabalhando em um aplicativo iOS desenvolvido no Swift e preciso armazenar algum conteúdo criado pelo usuário no dispositivo, mas não consigo encontrar uma maneira simples e rápida de armazenar / receber o conteúdo dos usuários no dispositivo.

Alguém poderia explicar como armazenar e acessar o armazenamento local?

A ideia é armazenar os dados quando o usuário executa uma ação e recebê-los quando o aplicativo é iniciado.

Nicklas Ridewing
fonte
Ei, bem-vindo, que tipo de dados você está armazenando? Você pode fornecer algum código para dar um exemplo do que você está procurando? Obrigado.
Wez
1
Os dados que eu preciso armazenar são basicamente apenas dados de string. Portanto, para simplificar, preciso salvar dois valores de String que eu possa receber se o usuário reiniciar o aplicativo.
Nicklas Ridewing
2
Você pode usar NSUserDefaults. Aqui está a informação que você precisa codingexplorer.com/nsuserdefaults-a-swift-introduction
bpolat

Respostas:

197

A solução mais simples se você estiver armazenando apenas duas strings é que NSUserDefaults, no Swift 3, essa classe foi renomeada para just UserDefaults.

É melhor armazenar suas chaves em algum lugar do mundo para que você possa reutilizá-las em outro lugar no seu código.

struct defaultsKeys {
    static let keyOne = "firstStringKey"
    static let keyTwo = "secondStringKey"
}

Swift 3.0, 4.0 e 5.0

// Setting

let defaults = UserDefaults.standard
defaults.set("Some String Value", forKey: defaultsKeys.keyOne)
defaults.set("Another String Value", forKey: defaultsKeys.keyTwo)

// Getting

let defaults = UserDefaults.standard
if let stringOne = defaults.string(forKey: defaultsKeys.keyOne) {
    print(stringOne) // Some String Value
}
if let stringTwo = defaults.string(forKey: defaultsKeys.keyTwo) {
    print(stringTwo) // Another String Value
}

Swift 2.0

// Setting

let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("Some String Value", forKey: defaultsKeys.keyOne)
defaults.setObject("Another String Value", forKey: defaultsKeys.keyTwo)

// Getting

let defaults = NSUserDefaults.standardUserDefaults()
if let stringOne = defaults.stringForKey(defaultsKeys.keyOne) {
    print(stringOne) // Some String Value
}
if let stringTwo = defaults.stringForKey(defaultsKeys.keyTwo) {
    print(stringTwo) // Another String Value
}

Para algo mais sério do que pequenas configurações, sinalizadores ou seqüências de base, você deve usar algum tipo de armazenamento persistente - Uma opção popular no momento é o Domínio, mas você também pode usar o SQLite ou o Apples CoreData .

Wez
fonte
7
Belo exemplo. Provavelmente deve ser uma enumeração em vez de uma estrutura.
pan-and-scan
3
Essa é uma implementação estranha de enum, se você tiver apenas constantes estáticas, também pode ser uma estrutura.
Craig Grummitt 14/03
1
Este código está errado. Não use os métodos KVC setValue(_:forKey:)para salvar dados em UserDefaults. Use os UserDefaultsmétodos fornecidos set(_:forKey:)em (no Swift 3).
rmaddy
Eu atualizei o código Swift 3. Não sei qual deve ser a sintaxe do Swift 2.
rmaddy
@ rmaddy está tudo bem como está.
Wez
57

Eles dizem usar NSUserDefaults

Quando eu estava implementando o armazenamento de dados de longo prazo (após o fechamento do aplicativo) pela primeira vez, tudo o que eu li on-line me indicou o NSUserDefaults. No entanto, eu queria armazenar um dicionário e, embora possível, estava provando ser uma dor. Passei horas tentando obter erros de tipo para desaparecer.

NSUserDefaults também é limitado em função

Outras leituras revelaram como a leitura / gravação de NSUserDefaults realmente força o aplicativo a ler / gravar tudo ou nada, tudo de uma vez, para que não seja eficiente. Aprendi então que recuperar uma matriz não é simples. Percebi que se você está armazenando mais do que algumas seqüências de caracteres ou booleanos, o NSUserDefaults realmente não é o ideal.

Também não é escalável. Se você estiver aprendendo a codificar, aprenda da maneira escalável. Use apenas NSUserDefaults para armazenar cadeias simples ou booleanos relacionados a preferências. Armazene matrizes e outros dados usando o Core Data, não é tão difícil quanto eles dizem. Apenas comece pequeno.

Atualização: Além disso, se você adicionar o suporte do Apple Watch, há outra consideração em potencial. Agora, os NSUserDefaults do seu aplicativo são enviados automaticamente para a extensão Watch.

Usando dados principais

Portanto, ignorei os avisos sobre o Core Data como uma solução mais difícil e comecei a ler. Dentro de três horas eu estava trabalhando. Eu tive minha matriz de tabelas sendo salva no Core Data e recarregando os dados ao abrir o aplicativo novamente! O código do tutorial foi fácil de adaptar e eu consegui armazenar as matrizes de título e detalhe com apenas um pouco de experiência extra.

Portanto, para qualquer pessoa que esteja lendo esta postagem que esteja enfrentando problemas do tipo NSUserDefault ou cuja necessidade seja mais do que armazenar strings, considere passar uma ou duas horas jogando com os dados principais.

Aqui está o tutorial que eu li:

http://www.raywenderlich.com/85578/first-core-data-app-using-swift

Se você não marcou "Dados Principais"

Se você não marcou "Dados Principais" ao criar seu aplicativo, pode adicioná-lo depois e leva apenas cinco minutos:

http://craig24.com/2014/12/how-to-add-core-data-to-an-existing-swift-project-in-xcode/

http://blog.zeityer.com/post/119012600864/adding-core-data-to-an-existing-swift-project

Como excluir das listas de dados principais

Excluir dados do Coredata Swift

Dave G
fonte
6
Há o meio termo de salvar seus dados em um arquivo .plist em algum lugar no diretório Documents ou em outro lugar no diretório em área restrita do aplicativo.
Nicolas Miari 6/08/2015
Sou novato, então leve essa pergunta com um pouco de sal, mas esse meio termo? De qualquer forma, o NSUserDefaults é praticamente um arquivo p.list, portanto (com base no que li) salvar em um arquivo p.list no diretório de documentos é o mesmo que usar o NSUserDefaults. A razão pela qual não usei esse método foi porque salvar uma matriz em uma p.list parecia envolver etapas extras, como convertê-la em outro tipo e depois, ao restaurar valores, era necessário redesigná-los depois de lê-los. da p.list. Mais uma vez, apenas com base na minha leitura nos últimos dias.
Dave G
Bem, salvar uma matriz para um arquivo .plist é simples bonita (todos os objetos na hierarquia são Arrays, Cordas, dicionários, ou Números (sem classes personalizadas).
Nicolas Miari
De acordo, a leitura do arquivo é tudo ou nada, mas você pode dividir seus dados em arquivos separados, dependendo da estrutura. E o CoreData teve uma curva de aprendizado significativa, pelo menos na última vez que verifiquei.
Nicolas Miari
3
Eu diria: use NSUserDefaults para armazenar preferências (afinal, é o que o nome significa), o chaveiro para armazenar dados persistentes e / ou sensíveis, seu esquema personalizado de arquivos .plist para dados gerados por aplicativos muito básicos e CoreData para coisas mais pesadas .
Nicolas Miari 6/08/2015
11

Okey, então obrigado a @bploat e o link para http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/

Descobri que a resposta é bastante simples para algum armazenamento básico de strings.

let defaults = NSUserDefaults.standardUserDefaults()

// Store
defaults.setObject("theGreatestName", forKey: "username")

// Receive
if let name = defaults.stringForKey("username")
{
    print(name)
    // Will output "theGreatestName"
}

Eu o resumi aqui http://ridewing.se/blog/save-local-data-in-swift/

Nicklas Ridewing
fonte
9

Usar NSCoding e NSKeyedArchiver é outra ótima opção para dados complexos demais NSUserDefaults, mas para os quais CoreData seria um exagero. Também oferece a oportunidade de gerenciar a estrutura de arquivos de forma mais explícita, o que é ótimo se você deseja usar a criptografia.

Patrick Lynch
fonte
7

Para o Swift 4.0, isso ficou mais fácil:

let defaults = UserDefaults.standard
//Set
defaults.set(passwordTextField.text, forKey: "Password")
//Get
let myPassword = defaults.string(forKey: "Password")
LevinsonTechnologies
fonte
6

Swift 3.0

Setter: armazenamento local

let authtoken = "12345"
    // Userdefaults helps to store session data locally 
 let defaults = UserDefaults.standard                                           
defaults.set(authtoken, forKey: "authtoken")

 defaults.synchronize()

Getter: armazenamento local

 if UserDefaults.standard.string(forKey: "authtoken") != nil {

//perform your task on success }
Ravindra Shekhawat
fonte
2
Você não deve salvar smth como o authtoken na UserDefaults..Please usar o Keychain para este
BilalReffas
5

Para Swift 3

UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")
Arun Yokesh
fonte
Não use métodos KVC para salvar dados.
rmaddy
4

Para alguém que não prefere lidar com os UserDefault por alguns motivos, há outra opção - NSKeyedArchiver e NSKeyedUnarchiver. Ajuda a salvar objetos em um arquivo usando o arquivador e a carregar arquivos arquivados em objetos originais.

// To archive object,
let mutableData: NSMutableData = NSMutableData()
let archiver: NSKeyedArchiver = NSKeyedArchiver(forWritingWith: mutableData)
archiver.encode(object, forKey: key)
archiver.finishEncoding()
return mutableData.write(toFile: path, atomically: true)

// To unarchive objects,
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
    let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
    let object = unarchiver.decodeObject(forKey: key)
}

Eu escrevi um utilitário simples para salvar / carregar objetos no armazenamento local, usei os códigos de amostra acima. Você pode querer ver isso. https://github.com/DragonCherry/LocalStorage

DragonCherry
fonte
4

Swift 5+

Nenhuma das respostas realmente cobre em detalhes o padrão construído nos recursos de armazenamento local. Pode fazer muito mais do que apenas strings.

Você tem as seguintes opções diretamente da documentação da apple para 'obter' dados dos padrões.

func object(forKey: String) -> Any?
//Returns the object associated with the specified key.

func url(forKey: String) -> URL?
//Returns the URL associated with the specified key.

func array(forKey: String) -> [Any]?
//Returns the array associated with the specified key.

func dictionary(forKey: String) -> [String : Any]?
//Returns the dictionary object associated with the specified key.

func string(forKey: String) -> String?
//Returns the string associated with the specified key.

func stringArray(forKey: String) -> [String]?
//Returns the array of strings associated with the specified key.

func data(forKey: String) -> Data?
//Returns the data object associated with the specified key.

func bool(forKey: String) -> Bool
//Returns the Boolean value associated with the specified key.

func integer(forKey: String) -> Int
//Returns the integer value associated with the specified key.

func float(forKey: String) -> Float
//Returns the float value associated with the specified key.

func double(forKey: String) -> Double
//Returns the double value associated with the specified key.

func dictionaryRepresentation() -> [String : Any]
//Returns a dictionary that contains a union of all key-value pairs in the domains in the search list.

Aqui estão as opções para 'configuração'

func set(Any?, forKey: String)
//Sets the value of the specified default key.

func set(Float, forKey: String)
//Sets the value of the specified default key to the specified float value.

func set(Double, forKey: String)
//Sets the value of the specified default key to the double value.

func set(Int, forKey: String)
//Sets the value of the specified default key to the specified integer value.

func set(Bool, forKey: String)
//Sets the value of the specified default key to the specified Boolean value.

func set(URL?, forKey: String)
//Sets the value of the specified default key to the specified URL.

Se estiver armazenando coisas como preferências e não dados grandes conjunto de , essas são opções perfeitamente perfeitas.

Exemplo duplo :

Configuração:

let defaults = UserDefaults.standard
var someDouble:Double = 0.5
defaults.set(someDouble, forKey: "someDouble")

Obtendo:

let defaults = UserDefaults.standard
var someDouble:Double = 0.0
someDouble = defaults.double(forKey: "someDouble")

O interessante de um dos getters é o dictionaryRepresentation , esse útil getter pega todos os seus tipos de dados, independentemente do que sejam, e os coloca em um bom dicionário que você pode acessar por seu nome de string e fornecer o tipo de dados correspondente correto quando você solicitar de volta, uma vez que é do tipo 'qualquer' .

Você pode armazenar suas próprias classes e objetos também usando os func set(Any?, forKey: String)botõesfunc object(forKey: String) -> Any? setter e getter de acordo.

Espero que isso esclareça mais o poder da classe UserDefaults para armazenar dados locais.

Na nota de quanto você deve armazenar e com que frequência, Hardy_Germany deu uma boa resposta sobre isso neste post , aqui está uma citação dele

Como muitos já mencionados: Não conheço nenhuma limitação de SIZE (exceto memória física) para armazenar dados em um .plist (por exemplo, UserDefaults). Portanto, não é uma questão de QUANTO.

A verdadeira questão deve ser: QUANTAS vezes você escreve valores novos / alterados ... E isso está relacionado ao consumo de bateria causado por essas gravações.

O IOS não tem chance de evitar uma gravação física no "disco" se um único valor for alterado, apenas para manter a integridade dos dados. Com relação aos padrões do usuário, isso faz com que o arquivo inteiro seja reescrito no disco.

Isso energiza o "disco", mantém-o ligado por mais tempo e evita que o IOS entre no estado de baixa energia.

Outra coisa a ser observada, conforme mencionado pelo usuário Mohammad Reza Farahani desta postagem, é a natureza assíncrona e síncrona dos userDefaults.

Quando você define um valor padrão, ele é alterado de forma síncrona no processo e de forma assíncrona para armazenamento persistente e outros processos.

Por exemplo, se você salvar e fechar rapidamente o programa, poderá notar que ele não salva os resultados, isso ocorre porque ele persiste de forma assíncrona. Você pode não perceber isso o tempo todo; portanto, se você planeja salvar antes de sair do programa, pode dar uma olhada nisso, dando-lhe algum tempo para concluir.

Talvez alguém tenha algumas soluções legais para isso que possam compartilhar nos comentários?

Joseph Astrahan
fonte
0

NsUserDefaults salva apenas tamanhos variáveis ​​pequenos. Se você deseja salvar muitos objetos, pode usar o CoreData como uma solução nativa ou criei uma biblioteca que ajuda a salvar objetos tão facilmente quanto a função .save (). É baseado em SQLite.

SundeedQLite

Confira e me conte seus comentários

nour sandid
fonte