¿Cómo guardar datos locales en una aplicación Swift?

149

Actualmente estoy trabajando en una aplicación iOS desarrollada en Swift y necesito almacenar algo de contenido creado por el usuario en el dispositivo, pero parece que no puedo encontrar una manera simple y rápida de almacenar / recibir el contenido de los usuarios en el dispositivo.

¿Alguien podría explicar cómo almacenar y acceder al almacenamiento local?

La idea es almacenar los datos cuando el usuario ejecuta una acción y recibirlos cuando se inicia la aplicación.

Nicklas Ridewing
fuente
Hola y bienvenido, ¿qué tipo de datos estás almacenando? ¿Puede proporcionar algún código para dar un ejemplo de lo que está buscando? Gracias.
Wez
1
Los datos que necesito almacenar son básicamente datos de cadena. Para que sea realmente simple, necesito guardar dos valores de cadena que pueda recibir si el usuario reinicia la aplicación.
Nicklas Ridewing
2
Puede usar NSUserDefaults. Aquí está la información que necesita codingexplorer.com/nsuserdefaults-a-swift-introduction
bpolat

Respuestas:

197

La solución más simple si solo está almacenando dos cadenas es que NSUserDefaultsen Swift 3 esta clase ha sido renombrada como justa UserDefaults.

Es mejor almacenar sus claves en algún lugar del mundo para poder reutilizarlas en otro lugar de su código.

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

Swift 3.0, 4.0 y 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 cualquier cosa más seria que una configuración menor, banderas o cadenas de base, debe usar algún tipo de almacén persistente: una opción popular en este momento es Realm, pero también puede usar SQLite o Apples CoreData .

Wez
fuente
77
Buen ejemplo Sin embargo, esto probablemente debería ser una enumeración en lugar de una estructura.
Pan-and-scan
3
Esa es una implementación extraña de enum, si solo tiene constantes estáticas, también podría ser una estructura.
Craig Grummitt
1
Este código está mal No use los métodos KVC setValue(_:forKey:)para guardar datos en UserDefaults. Utilice los UserDefaultsmétodos provistos de set(_:forKey:)(en Swift 3).
rmaddy
Actualicé el código Swift 3. No sé cuál debería ser la sintaxis de Swift 2.
rmaddy
@rmaddy está bien como está.
Wez
57

Dicen que use NSUserDefaults

Cuando estaba implementando el almacenamiento de datos a largo plazo (después del cierre de la aplicación) por primera vez, todo lo que leí en línea me indicó NSUserDefaults. Sin embargo, quería almacenar un diccionario y, aunque era posible, estaba resultando un dolor. Pasé horas intentando que los errores de tipo desaparecieran.

NSUserDefaults también tiene una función limitada

La lectura adicional reveló cómo la lectura / escritura de NSUserDefaults realmente obliga a la aplicación a leer / escribir todo o nada, todo a la vez, por lo que no es eficiente. Luego aprendí que recuperar una matriz no es sencillo. Me di cuenta de que si está almacenando más de unas pocas cadenas o booleanos, NSUserDefaults realmente no es ideal.

Tampoco es escalable. Si está aprendiendo a codificar, aprenda de forma escalable. Utilice solo NSUserDefaults para almacenar cadenas simples o booleanos relacionados con las preferencias. Almacene matrices y otros datos utilizando Core Data, no es tan difícil como dicen. Solo comienza pequeño.

Actualización: Además, si agrega soporte para Apple Watch, hay otra consideración potencial. NSUserDefaults de su aplicación ahora se envía automáticamente a Watch Extension.

Usando datos básicos

Así que ignoré las advertencias sobre Core Data como una solución más difícil y comencé a leer. En tres horas lo tuve funcionando. ¡Tenía mi matriz de tablas guardada en Core Data y volví a cargar los datos al abrir la aplicación nuevamente! El código del tutorial fue bastante fácil de adaptar y pude hacer que almacenara matrices de títulos y detalles con solo un poco de experimentación adicional.

Entonces, para cualquiera que lea esta publicación que tenga problemas con los problemas de tipo NSUserDefault o cuya necesidad sea más que almacenar cadenas, considere pasar una o dos horas jugando con los datos principales.

Aquí está el tutorial que leí:

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

Si no marcó "Datos principales"

Si no marcó "Datos principales" cuando creó su aplicación, puede agregarla después y solo le llevará 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

Cómo eliminar de las listas de datos principales

Eliminar datos de Coredata Swift

Dave G
fuente
66
Existe un término medio para guardar sus datos en un archivo .plist en algún lugar del directorio Documentos o en otro lugar en el directorio de espacio aislado de la aplicación.
Nicolas Miari
Soy un novato, así que tome esta pregunta con un grano de sal, pero ¿es eso un término medio? De todos modos, NSUserDefaults es prácticamente un archivo p.list, por lo que (según lo que he leído) guardar en un archivo p.list en el directorio de documentos es lo mismo que usar NSUserDefaults. La razón por la que no utilicé ese método fue porque guardar una matriz en una lista p.sonaba como si implicara pasos adicionales, como convertirlo a otro tipo y luego, al restaurar los valores, requería reasignar los valores después de leerlos. de la lista de p. De nuevo, únicamente basado en mi lectura en los últimos días.
Dave G
Bueno, guardar una matriz en un archivo .plist es bastante sencillo (todos los objetos en la jerarquía son matrices, cadenas, diccionarios o números (sin clases personalizadas).
Nicolas Miari
De acuerdo, leer el archivo es todo o nada, pero puede dividir sus datos en archivos separados dependiendo de su estructura. Y CoreData tenía una curva de aprendizaje significativa, al menos la última vez que lo revisé.
Nicolas Miari
3
Yo diría: use NSUserDefaults para almacenar preferencias (eso es lo que significa el nombre después de todo), El llavero para almacenar datos persistentes y / o confidenciales, su esquema personalizado de archivos .plist para datos generados por aplicaciones muy básicas y CoreData para cosas más pesadas .
Nicolas Miari
11

Okey, gracias a @bploat y al enlace a http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/

Descubrí que la respuesta es bastante simple para un almacenamiento básico de cadenas.

let defaults = NSUserDefaults.standardUserDefaults()

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

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

Lo resumí aquí http://ridewing.se/blog/save-local-data-in-swift/

Nicklas Ridewing
fuente
9

Usar NSCoding y NSKeyedArchiver es otra gran opción para los datos que son demasiado complejos NSUserDefaults, pero para los cuales CoreData sería excesivo. También le brinda la oportunidad de administrar la estructura de archivos de manera más explícita, lo cual es excelente si desea utilizar el cifrado.

Patrick Lynch
fuente
7

Para Swift 4.0, esto se hizo más fácil:

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

Swift 3.0

Setter: almacenamiento local

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

 defaults.synchronize()

Getter: almacenamiento local

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

//perform your task on success }
Ravindra Shekhawat
fuente
2
No debe guardar algo como el authtoken en UserDefaults ... Utilice el llavero para esto
BilalReffas
5

Para Swift 3

UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")
Arun Yokesh
fuente
No use métodos KVC para guardar datos.
rmaddy
4

Para alguien que no preferiría manejar UserDefaults por alguna razón, hay otra opción: NSKeyedArchiver y NSKeyedUnarchiver. Ayuda a guardar objetos en un archivo usando el archivador y cargar archivos archivados en objetos originales.

// 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)
}

Escribí una utilidad simple para guardar / cargar objetos en el almacenamiento local, utilicé los códigos de muestra anteriores. Es posible que desee ver esto. https://github.com/DragonCherry/LocalStorage

DragonCherry
fuente
4

Swift 5+

Ninguna de las respuestas cubre en detalle las capacidades de almacenamiento local integradas por defecto. Puede hacer mucho más que solo cadenas.

Tiene las siguientes opciones directamente de la documentación de Apple para 'obtener' datos de los valores predeterminados.

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.

Estas son las opciones para 'configurar'

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.

Si almacena cosas como preferencias y no un conjunto de datos grande, estas son opciones perfectamente adecuadas.

Doble ejemplo :

Ajuste:

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

Consiguiendo:

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

Lo interesante de uno de los captadores es la representación del diccionario , este práctico captador tomará todos sus tipos de datos independientemente de cuáles sean y los colocará en un buen diccionario al que puede acceder por su nombre de cadena y le dará el tipo de datos correspondiente correcto cuando solicite lo devuelve ya que es del tipo 'any' .

Puede almacenar sus propias clases y objetos también utilizando el func set(Any?, forKey: String)y func object(forKey: String) -> Any?setter y getter en consecuencia.

Espero que esto aclare más el poder de la clase UserDefaults para almacenar datos locales.

Sobre la nota de cuánto debe almacenar y con qué frecuencia, Hardy_Germany dio una buena respuesta al respecto en esta publicación , aquí hay una cita de él

Como muchos ya mencionaron: no conozco ninguna limitación de TAMAÑO (excepto la memoria física) para almacenar datos en un .plist (por ejemplo, UserDefaults). Entonces no se trata de CUÁNTO.

La verdadera pregunta debería ser CUÁNTAS veces escribe valores nuevos / modificados ... Y esto está relacionado con el consumo de batería que esto causará.

IOS no tiene ninguna posibilidad de evitar una escritura física en el "disco" si cambia un solo valor, solo para mantener la integridad de los datos. En cuanto a los valores predeterminados del usuario, esto hace que todo el archivo se reescriba en el disco.

Esto enciende el "disco" y lo mantiene encendido durante más tiempo y evita que el IOS pase al estado de baja energía.

Algo más a tener en cuenta según lo mencionado por el usuario Mohammad Reza Farahani de esta publicación es la naturaleza asincrónica y sincrónica de los valores predeterminados del usuario.

Cuando establece un valor predeterminado, se cambia sincrónicamente dentro de su proceso y de forma asíncrona a almacenamiento persistente y otros procesos.

Por ejemplo, si guarda y cierra rápidamente el programa, puede notar que no guarda los resultados, esto se debe a que persiste de forma asincrónica. Es posible que no lo note todo el tiempo, por lo que si planea ahorrar antes de salir del programa, es posible que desee dar cuenta de esto dándole un poco de tiempo para terminar.

¿Quizás alguien tiene algunas buenas soluciones para esto que pueden compartir en los comentarios?

Joseph Astrahan
fuente
0

NsUserDefaults guarda solo tamaños variables pequeños. Si desea guardar muchos objetos, puede usar CoreData como una solución nativa, o he creado una biblioteca que lo ayuda a guardar objetos tan fácilmente como la función .save (). Está basado en SQLite.

SundeedQLite

Compruébalo y dime tus comentarios

nour sandid
fuente