Wie speichere ich lokale Daten in einer Swift-App?

148

Ich arbeite derzeit an einer in Swift entwickelten iOS-App und muss einige vom Benutzer erstellte Inhalte auf dem Gerät speichern, aber ich kann anscheinend keine einfache und schnelle Möglichkeit finden, die Benutzerinhalte auf dem Gerät zu speichern / zu empfangen.

Könnte jemand erklären, wie man lokalen Speicher speichert und darauf zugreift?

Die Idee ist, die Daten zu speichern, wenn der Benutzer eine Aktion ausführt, und sie zu empfangen, wenn die App gestartet wird.

Nicklas Ridewing
quelle
Hey und willkommen, welche Art von Daten speichern Sie? Können Sie einen Code angeben, der ein Beispiel für das gibt, wonach Sie suchen? Vielen Dank.
Wez
1
Die Daten, die ich speichern muss, sind im Grunde nur Zeichenfolgendaten. Um es wirklich einfach zu halten, muss ich zwei String-Werte speichern, die ich erhalten kann, wenn der Benutzer die App neu startet.
Nicklas Ridewing
2
Sie können NSUserDefaults verwenden. Hier sind die Informationen, die Sie benötigen codingexplorer.com/nsuserdefaults-a-swift-introduction
bpolat

Antworten:

197

Die einfachste Lösung, wenn Sie nur zwei Zeichenfolgen speichern, ist NSUserDefaults , dass diese Klasse in Swift 3 in just umbenannt wurde UserDefaults.

Es ist am besten, Ihre Schlüssel irgendwo global zu speichern, damit Sie sie an anderer Stelle in Ihrem Code wiederverwenden können.

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

Swift 3.0, 4.0 & 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
}

Für alles, was schwerwiegender ist als kleinere Konfigurationen, Flags oder Basiszeichenfolgen, sollten Sie einen dauerhaften Speicher verwenden. Eine beliebte Option ist derzeit Realm, aber Sie können auch SQLite oder Apples eigene CoreData verwenden .

Wez
quelle
7
Schönes Beispiel. Dies sollte jedoch wahrscheinlich eine Aufzählung anstelle einer Struktur sein.
Pan-and-Scan
3
Das ist eine seltsame Implementierung von enum. Wenn Sie nur statische Konstanten haben, kann es sich auch um eine Struktur handeln.
Craig Grummitt
1
Dieser Code ist falsch. Verwenden Sie nicht die KVC-Methoden setValue(_:forKey:), um Daten in UserDefaults zu speichern. Verwenden Sie die bereitgestellten UserDefaultsMethoden von set(_:forKey:)(in Swift 3).
rmaddy
Ich habe den Swift 3-Code aktualisiert. Ich weiß nicht, wie die Swift 2-Syntax lauten soll.
rmaddy
@rmaddy es ist in Ordnung wie es ist.
Wez
57

Sie sagen, verwenden Sie NSUserDefaults

Als ich zum ersten Mal eine langfristige Datenspeicherung (nach dem Schließen der App) implementierte, wies mich alles, was ich online las, auf NSUserDefaults hin. Ich wollte jedoch ein Wörterbuch speichern, und obwohl dies möglich war, erwies es sich als schmerzhaft. Ich habe stundenlang versucht, Tippfehler zu beseitigen.

NSUserDefaults ist ebenfalls in seiner Funktion eingeschränkt

Weiteres Lesen zeigte, wie das Lesen / Schreiben von NSUserDefaults die App wirklich zwingt, alles oder nichts auf einmal zu lesen / schreiben, so dass es nicht effizient ist. Dann habe ich gelernt, dass das Abrufen eines Arrays nicht einfach ist. Ich habe festgestellt, dass NSUserDefaults wirklich nicht ideal ist, wenn Sie mehr als ein paar Zeichenfolgen oder Boolesche Werte speichern.

Es ist auch nicht skalierbar. Wenn Sie das Codieren lernen, lernen Sie die skalierbare Methode. Verwenden Sie NSUserDefaults nur zum Speichern einfacher Zeichenfolgen oder Boolescher Werte, die sich auf Einstellungen beziehen. Speichern Sie Arrays und andere Daten mit Core Data. Es ist nicht so schwierig, wie sie sagen. Fangen Sie einfach klein an.

Update: Wenn Sie Apple Watch-Unterstützung hinzufügen, gibt es noch eine weitere mögliche Überlegung. Die NSUserDefaults Ihrer App werden jetzt automatisch an die Watch Extension gesendet.

Verwenden von Kerndaten

Daher ignorierte ich die Warnungen, dass Core Data eine schwierigere Lösung sei, und begann zu lesen. Innerhalb von drei Stunden hatte ich es funktioniert. Ich hatte mein Tabellenarray in Core Data gespeichert und lud die Daten beim Öffnen der App wieder hoch! Der Tutorial-Code war einfach genug anzupassen und ich konnte ihn mit nur ein wenig zusätzlichem Experimentieren sowohl Titel- als auch Detail-Arrays speichern lassen.

Wenn Sie diesen Beitrag lesen und Probleme mit dem Typ NSUserDefault haben oder mehr als nur Zeichenfolgen speichern müssen, sollten Sie ein oder zwei Stunden mit Kerndaten spielen.

Hier ist das Tutorial, das ich gelesen habe:

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

Wenn Sie "Kerndaten" nicht überprüft haben

Wenn Sie beim Erstellen Ihrer App "Kerndaten" nicht aktiviert haben, können Sie sie nachträglich hinzufügen. Dies dauert nur fünf Minuten:

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

Löschen aus Kerndatenlisten

Daten aus Coredata Swift löschen

Dave G.
quelle
6
Es gibt den Mittelweg, Ihre Daten in einer .plist-Datei irgendwo im Dokumentverzeichnis oder an anderer Stelle im Sandbox-Verzeichnis der App zu speichern.
Nicolas Miari
Ich bin ein Neuling, also nimm diese Frage mit einem Körnchen Salz, aber ist das Mittelweg? NSUserDefaults ist sowieso praktisch eine p.list-Datei, daher entspricht das Speichern in einer p.list-Datei im Dokumentenverzeichnis (basierend auf dem, was ich gelesen habe) der Verwendung von NSUserDefaults. Der Grund, warum ich diese Methode nicht verwendet habe, war, dass das Speichern eines Arrays in einer p.list zusätzliche Schritte erforderte, z. B. das Konvertieren in einen anderen Typ, und später beim Wiederherstellen von Werten die Werte nach dem Lesen neu zugewiesen werden mussten von der p.list. Wieder nur aufgrund meiner Lektüre in den letzten Tagen.
Dave G
Nun, das Speichern eines Arrays in einer .plist-Datei ist ziemlich einfach (alle Objekte in der Hierarchie sind Arrays, Strings, Wörterbücher oder Zahlen (keine benutzerdefinierten Klassen).
Nicolas Miari
Einverstanden ist das Lesen der Datei alles oder nichts, aber Sie können Ihre Daten je nach Struktur in separate Dateien aufteilen. Und CoreData hatte zumindest beim letzten Mal eine signifikante Lernkurve.
Nicolas Miari
3
Ich würde sagen: Verwenden Sie NSUserDefaults zum Speichern von Einstellungen (das bedeutet schließlich der Name), den Schlüsselbund zum Speichern persistenter und / oder vertraulicher Daten, Ihr benutzerdefiniertes Schema von .plist-Dateien für sehr einfache , von Apps generierte Daten und CoreData für schwerere Inhalte .
Nicolas Miari
11

Okey also danke an @bploat und den Link zu http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/

Ich habe festgestellt, dass die Antwort für einige grundlegende Zeichenfolgenspeicher recht einfach ist.

let defaults = NSUserDefaults.standardUserDefaults()

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

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

Ich habe es hier zusammengefasst http://ridewing.se/blog/save-local-data-in-swift/

Nicklas Ridewing
quelle
9

Die Verwendung von NSCoding und NSKeyedArchiver ist eine weitere großartige Option für Daten, für die zu komplex NSUserDefaultsist, für die CoreData jedoch übertrieben wäre. Sie haben auch die Möglichkeit, die Dateistruktur expliziter zu verwalten. Dies ist ideal, wenn Sie die Verschlüsselung verwenden möchten.

Patrick Lynch
quelle
7

Für Swift 4.0 wurde dies einfacher:

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

Swift 3.0

Setter: Lokaler Speicher

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

 defaults.synchronize()

Getter: Lokale Speicherung

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

//perform your task on success }
Ravindra Shekhawat
quelle
2
Sie sollten etw nicht wie das Authtoken in den UserDefaults speichern. Bitte verwenden Sie den Schlüsselbund dafür
BilalReffas
5

Für Swift 3

UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")
Arun Yokesh
quelle
Verwenden Sie keine KVC-Methoden zum Speichern von Daten.
rmaddy
4

Für jemanden, der aus bestimmten Gründen nicht lieber mit UserDefaults umgehen möchte, gibt es eine andere Option - NSKeyedArchiver & NSKeyedUnarchiver. Es hilft dabei, Objekte mithilfe des Archivierungsprogramms in einer Datei zu speichern und archivierte Dateien in Originalobjekte zu laden.

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

Ich habe ein einfaches Dienstprogramm zum Speichern / Laden von Objekten im lokalen Speicher geschrieben, das oben verwendete Beispielcodes verwendet. Vielleicht möchten Sie das sehen. https://github.com/DragonCherry/LocalStorage

DragonCherry
quelle
3

Swift 5+

Keine der Antworten behandelt die im Standard integrierten lokalen Speicherfunktionen wirklich im Detail. Es kann weit mehr als nur Saiten.

Sie haben die folgenden Optionen direkt aus der Apple-Dokumentation, um Daten aus den Standardeinstellungen abzurufen.

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.

Hier sind die Optionen für die Einstellung

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.

Wenn Dinge wie Einstellungen und keine großen Daten gespeichert werden setzen diese sind völlig in Ordnung Optionen.

Doppelbeispiel :

Rahmen:

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

Bekommen:

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

Das Interessante an einem der Getter ist dictionaryRepresentation . Dieser praktische Getter verwendet alle Ihre Datentypen, unabhängig davon, um welche es sich handelt, und fügt sie in ein schönes Wörterbuch ein, auf das Sie über den Namen der Zeichenfolge zugreifen können, und gibt den richtigen Datentyp an, wenn Sie danach fragen es zurück, da es vom Typ 'any' ist .

Sie können Ihre eigenen Klassen und Objekte auch mit func set(Any?, forKey: String)und speichernfunc object(forKey: String) -> Any? dementsprechend Setter und Getter.

Ich hoffe, dies verdeutlicht die Leistungsfähigkeit der UserDefaults-Klasse zum Speichern lokaler Daten.

In Anbetracht dessen, wie viel Sie speichern sollten und wie oft, gab Hardy_Germany in diesem Beitrag eine gute Antwort darauf . Hier ist ein Zitat daraus

Wie viele bereits erwähnt haben: Mir ist keine Größenbeschränkung (außer physischem Speicher) zum Speichern von Daten in einer .plist bekannt (z. B. UserDefaults). Es geht also nicht darum, wie viel.

Die eigentliche Frage sollte sein, wie oft Sie neue / geänderte Werte schreiben ... Und dies hängt mit dem Batterieverbrauch zusammen, den diese Schreibvorgänge verursachen.

IOS hat keine Chance, ein physisches Schreiben auf "Festplatte" zu vermeiden, wenn sich ein einzelner Wert ändert, nur um die Datenintegrität zu gewährleisten. In Bezug auf UserDefaults führt dies dazu, dass die gesamte Datei auf die Festplatte umgeschrieben wird.

Dies schaltet die "Festplatte" ein und hält sie länger eingeschaltet und verhindert, dass IOS in den Energiesparmodus wechselt.

Etwas anderes zu beachten, wie von Benutzer Mohammad Reza Farahani in diesem Beitrag erwähnt, ist die asynchrone und synchrone Natur von userDefaults.

Wenn Sie einen Standardwert festlegen, wird dieser synchron in Ihrem Prozess und asynchron in persistenten Speicher und andere Prozesse geändert.

Wenn Sie beispielsweise das Programm speichern und schnell schließen, werden Sie möglicherweise feststellen, dass die Ergebnisse nicht gespeichert werden. Dies liegt daran, dass es asynchron fortbesteht. Möglicherweise bemerken Sie dies nicht immer. Wenn Sie also vor dem Beenden des Programms speichern möchten, sollten Sie dies berücksichtigen, indem Sie ihm etwas Zeit zum Beenden geben.

Vielleicht hat jemand ein paar nette Lösungen dafür, die er in den Kommentaren teilen kann?

Joseph Astrahan
quelle
0

NsUserDefaults speichert nur kleine variable Größen. Wenn Sie viele Objekte speichern möchten, können Sie CoreData als native Lösung verwenden, oder ich habe eine Bibliothek erstellt, mit der Sie Objekte so einfach wie die Funktion .save () speichern können. Es basiert auf SQLite.

SundeedQLite

Schau es dir an und sag mir deine Kommentare

nour sandid
quelle