Come salvare i dati locali in un'app Swift?


149

Attualmente sto lavorando su un'app iOS sviluppata in Swift e ho bisogno di archiviare alcuni contenuti creati dall'utente sul dispositivo, ma non riesco a trovare un modo semplice e veloce per archiviare / ricevere i contenuti degli utenti sul dispositivo.

Qualcuno potrebbe spiegare come archiviare e accedere all'archiviazione locale?

L'idea è di archiviare i dati quando l'utente esegue un'azione e di riceverli all'avvio dell'app.


Ciao e benvenuto, che tipo di dati stai memorizzando? Potete fornire un codice per dare un esempio di ciò che state cercando? Grazie.
Wez,

1
I dati che devo memorizzare sono fondamentalmente solo dati stringa. Quindi, per mantenerlo davvero semplice, devo salvare due valori String che posso ricevere se l'utente riavvia l'app.
Nicklas Ridewing,

2
È possibile utilizzare NSUserDefaults. Ecco le informazioni di cui hai bisogno per codingexplorer.com/nsuserdefaults-a-swift-introduction
bpolat

Risposte:


197

La soluzione più semplice se stai solo memorizzando due stringhe è NSUserDefaults, in Swift 3 questa classe è stata rinominata in just UserDefaults.

È meglio archiviare le chiavi in ​​qualche luogo a livello globale in modo da poterle riutilizzare altrove nel codice.

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
}

Per qualsiasi cosa più seria della configurazione minore, flag o stringhe di base dovresti usare una sorta di archivio persistente - Un'opzione popolare al momento è Realm ma puoi anche usare CoreData proprio SQLite o Mele .


7
Bell'esempio Questo dovrebbe probabilmente essere un enum invece di una struttura però.
pan-and-scan

3
Questa è una strana implementazione di enum, se hai solo costanti statiche, potrebbe anche essere una struttura.
Craig Grummitt,

1
Questo codice è sbagliato Non utilizzare i metodi KVC setValue(_:forKey:)per salvare i dati in UserDefaults. Utilizzare i UserDefaultsmetodi forniti di set(_:forKey:)(in Swift 3).
rmaddy,

Ho aggiornato il codice Swift 3. Non so quale dovrebbe essere la sintassi di Swift 2.
rmaddy,

@rmaddy va bene così com'è.
Wez,

57

Dicono Usa NSUserDefaults

Quando stavo implementando l'archiviazione dei dati a lungo termine (dopo la chiusura dell'app) per la prima volta, tutto ciò che leggevo online mi indirizzava verso NSUserDefaults. Tuttavia, volevo memorizzare un dizionario e, sebbene possibile, si stava dimostrando una seccatura. Ho passato ore a cercare di ottenere errori di tipo per andare via.

NSUserDefaults ha anche funzioni limitate

Ulteriori letture hanno rivelato come la lettura / scrittura di NSUserDefaults costringa davvero l'app a leggere / scrivere tutto o niente, tutto in una volta, quindi non è efficiente. Poi ho imparato che il recupero di un array non è semplice. Mi sono reso conto che se stai memorizzando più di qualche stringa o valore booleano, NSUserDefaults non è proprio l'ideale.

Inoltre non è scalabile. Se stai imparando come programmare, impara in modo scalabile. Utilizzare NSUserDefaults solo per la memorizzazione di stringhe semplici o valori booleani relativi alle preferenze. Archivia array e altri dati utilizzando Core Data, non è così difficile come si dice. Basta iniziare in piccolo.

Aggiornamento: Inoltre, se aggiungi il supporto Apple Watch, c'è un'altra potenziale considerazione. NSUserDefaults della tua app ora viene automaticamente inviato all'estensione dell'orologio.

Utilizzo dei dati di base

Quindi ho ignorato gli avvertimenti sul fatto che Core Data fosse una soluzione più difficile e ho iniziato a leggere. Entro tre ore l'ho fatto funzionare. Ho avuto il mio array di tabelle salvato in Core Data e ricaricato i dati all'apertura del backup dell'app! Il codice tutorial è stato abbastanza facile da adattare e sono stato in grado di memorizzarlo sia per il titolo che per i dettagli con solo un piccolo esperimento in più.

Quindi, per chiunque legga questo post che è alle prese con problemi di tipo NSUserDefault o il cui bisogno è più che archiviare stringhe, considera di passare un'ora o due a giocare con i dati di base.

Ecco il tutorial che ho letto:

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

Se non hai selezionato "Dati principali"

Se non hai selezionato "Dati principali" quando hai creato l'app, puoi aggiungerla dopo e ci vogliono solo cinque minuti:

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

Come eliminare dagli elenchi di dati principali

Elimina dati da Coredata Swift


6
C'è la via di mezzo per salvare i tuoi dati in un file .plist da qualche parte nella directory Documenti o altrove nella directory sandbox dell'app.
Nicolas Miari,

Sono un principiante, quindi prendi questa domanda con un granello di sale, ma è quella via di mezzo? NSUserDefaults è praticamente un file p.list comunque, quindi (in base a ciò che ho letto) il salvataggio in un file p.list nella directory dei documenti è lo stesso dell'uso di NSUserDefaults. Il motivo per cui non ho usato quel metodo è perché il salvataggio di un array in una p.list sembrava comportare passaggi aggiuntivi, come la conversione in un altro tipo e, successivamente, durante il ripristino dei valori, ha richiesto la riassegnazione dei valori dopo averli letti dall'elenco p. Ancora una volta, basandomi esclusivamente sulla mia lettura degli ultimi giorni.
Dave G,

Bene, il salvataggio di un array in un file .plist è piuttosto semplice (tutti gli oggetti nella gerarchia sono array, stringhe, dizionari o numeri (senza classi personalizzate).
Nicolas Miari

D'accordo, leggere il file è tutto o niente, ma puoi dividere i tuoi dati in file separati a seconda della sua struttura. E CoreData ha avuto una curva di apprendimento significativa, almeno l'ultima volta che ho verificato.
Nicolas Miari,

3
Direi: usa NSUserDefaults per memorizzare le preferenze (questo è ciò che significa il nome dopo tutto), Il portachiavi per archiviare dati persistenti e / o sensibili, il tuo schema personalizzato di file .plist per dati generati da app di base e CoreData per cose più pesanti .
Nicolas Miari,

11

Okey grazie a @bploat e al link a http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/

Ho trovato che la risposta è abbastanza semplice per un po 'di archiviazione delle stringhe di base.

let defaults = NSUserDefaults.standardUserDefaults()

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

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

L'ho riassunto qui http://ridewing.se/blog/save-local-data-in-swift/


9

L'uso di NSCoding e NSKeyedArchiver è un'altra grande opzione per i dati per i quali è troppo complessa NSUserDefaults, ma per cui CoreData sarebbe eccessivo. Ti dà anche l'opportunità di gestire la struttura dei file in modo più esplicito, il che è fantastico se vuoi usare la crittografia.


7

Per Swift 4.0, questo è diventato più semplice:

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

6

Swift 3.0

Setter: archiviazione locale

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

 defaults.synchronize()

Getter: archiviazione locale

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

//perform your task on success }

2
Non dovresti salvare smth come il authtoken in UserDefaults..Per favore usa il Keychain per questo
BilalReffas,

5

Per Swift 3

UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")

Non utilizzare i metodi KVC per salvare i dati.
rmaddy,

4

Per qualcuno che non preferisce gestire UserDefaults per alcuni motivi, c'è un'altra opzione: NSKeyedArchiver e NSKeyedUnarchiver. Aiuta a salvare oggetti in un file usando l'archiviatore e carica il file archiviato su oggetti originali.

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

Ho scritto una semplice utility per salvare / caricare oggetti nella memoria locale, ho usato i codici di esempio sopra. Potresti voler vedere questo. https://github.com/DragonCherry/LocalStorage


4

Swift 5+

Nessuna delle risposte copre in dettaglio le funzionalità di archiviazione locale predefinite. Può fare molto più che semplici stringhe.

Sono disponibili le seguenti opzioni direttamente dalla documentazione di Apple per "ottenere" i dati dalle impostazioni predefinite.

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.

Ecco le opzioni per 'impostazione'

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 stai memorizzando cose come le preferenze e non un set di dati di grandi dimensioni, queste sono opzioni perfettamente valide.

Doppio esempio :

Ambientazione:

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

ottenere:

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

Ciò che è interessante di uno dei getter è il dizionario di rappresentazione , questo pratico getter prenderà tutti i tuoi tipi di dati indipendentemente da ciò che sono e li inserirà in un bel dizionario a cui puoi accedere con il nome della stringa e fornire il tipo di dati corrispondente corretto quando chiedi indietro poiché è di tipo "qualsiasi" .

È possibile memorizzare le proprie classi e oggetti anche utilizzando func set(Any?, forKey: String)e il func object(forKey: String) -> Any?setter e il getter di conseguenza.

Spero che questo chiarisca maggiormente la potenza della classe UserDefaults per la memorizzazione di dati locali.

Sulla nota di quanto dovresti conservare e con quale frequenza, Hardy_Germany ha dato una buona risposta su questo post , ecco una citazione da esso

Come molti hanno già detto: non sono a conoscenza di alcuna limitazione SIZE (eccetto la memoria fisica) per archiviare i dati in un .plist (es. UserDefaults). Quindi non è una questione di QUANTO.

La vera domanda dovrebbe essere QUANTO SPESSO si scrivono valori nuovi / modificati ... E questo è legato al consumo della batteria che questo scriverà causerà.

IOS non ha alcuna possibilità di evitare una scrittura fisica su "disco" se un singolo valore viene modificato, solo per mantenere l'integrità dei dati. Per quanto riguarda UserDefaults, questo causa l'intero file riscritto sul disco.

Questo alimenta il "disco" e lo mantiene acceso per un tempo più lungo e impedisce a IOS di passare allo stato di risparmio energetico.

Qualcos'altro da notare, come menzionato dall'utente Mohammad Reza Farahani da questo post, è la natura asincrona e sincrona dell'utente Default.

Quando si imposta un valore predefinito, viene modificato in modo sincrono all'interno del processo e in modo asincrono alla memoria permanente e ad altri processi.

Ad esempio, se si salva e si chiude rapidamente il programma, è possibile notare che non salva i risultati, perché persiste in modo asincrono. Potresti non accorgertene sempre, quindi se prevedi di salvare prima di abbandonare il programma, potresti tenerne conto dandogli un po 'di tempo per terminare.

Forse qualcuno ha delle belle soluzioni per questo che può condividere nei commenti?


0

NsUserDefaults salva solo piccole dimensioni variabili. Se vuoi salvare molti oggetti puoi usare CoreData come soluzione nativa, oppure ho creato una libreria che ti aiuta a salvare gli oggetti con la stessa facilità .save (). Si basa su SQLite.

SundeedQLite

Dai un'occhiata e dimmi i tuoi commenti

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.