nsuserdefaults

Das Cocoa- und CocoaTouch Framework bieten für Benutzereinstellungen die Klasse NSUserDefaults. Sie ist eine zentralen Lösung für Einstellungen und speichert Key/Value Paare. Verschiedene Datentypen können einem bestimmten Schlüssel zugeordnet, persistent gespeichert und später wieder abgerufen werden. Das erspart Dir eine eigene Implementierung der gleichen Idee, die sonst in nahezu jeder App notwendig wäre. In diesem Artikel lernst Du die User Defaults, ihre Möglichkeiten und best-practices kennen. Im NSUserDefaults Tutorial erstellst Du sogar einen Screen für Settings.

Worum geht es?

Wofür werden die NSUserDefaults verwendet?

Der Name User Defaults könnte streng mit Benutzereinstellungen übersetzt werden. Gespeichert werden aber nicht nur bewusst gewählte Einstellungen wie z.B. Push-Benachrichtigungen (ja/nein), eine Hintergrundfarbe, die Lautstärke oder ähnliches. Auch Nutzdaten, die bei der Arbeit mit einer App anfallen, können darin wunderbar abgelegt und beim nächsten Start wieder geladen werden.

Das kann zum Beispiel die eingestellte Dauer bei einem Timer sein. Beim nächsten Start wird der verwendete Wert wiederhergestellt. Auch ein Vermerk, ob die App zum ersten Mal gestartet wurde, wäre denkbar. So können dem Benutzer hilfreiche Informationen eingeblendet werden – es sei über die NSUserDefaults wird der entsprechende Hinweis gefunden. Oder der zuletzt angesehen Datensatz? Auch solche Daten können sehr einfach in den User Defaults abgelegt und wiederhergestellt werden.

Du kannst also auch Daten zur Laufzeit abgelegen. Es handelt sich aber nicht um eine Datenbank. Wenn Werte hinterlegt werden, sollten sie isoliert und als einzelner Eintrag sinnvoll sein. Fallen immer wieder die gleichen Daten an, ist CoreData vielleicht die bessere Technologie.

Datentypen für NSUserDefaults

Für verschiedene Daten bietet die Klasse NSUserDefaults einen passenden Typ. Die Unterstützung gibt es bereits seit iOS 2.0 und daher sind auch die Datentypen an Objective-C angelehnt. Gespeichert werden können:

  • NSString
  • NSNumber
  • NSDate
  • NSArray
  • NSDictionary
  • NSData

Für jeden Typ gibt es passende Methoden zum Schreiben und Lesen, die von der Klasse NSUserDefaults angeboten werden. Doch vorher wird eine Referenz auf die User Defaults benötigt.

Eine Referenz auf das Objekt

Bei der Arbeit mit den User Defaults erzeugst Du die Instanz von NSUserDefaults nicht selbst, sondern holst Dir eine Referenz mit der Methode standardUserDefaults(). Es ist eine statische Methode, die direkt auf der Klasse aufgerufen wird:

Über die Konstante „defaults“ werden Werte wahlweise gespeichert oder abgerufen.

Werte in den User Defaults speichern

Um Werte zu speichern rufst Du die für den jeweiligen Typ passende Methode auf. Der erste Parameter ist der zu speichernde Wert, die zweite Information ist der Schlüssel unter dem der Wert abgelegt wird. Den kannst Du frei wählen. Er muss nur eindeutig sein. Im folgenden Beispiel speicherst Du boolesche Werte (Bool) sowie eine Ganzzahl (Int):

Nach dem gleichen Schema kannst Du die weiteren Methoden aufrufen. Das sind:

  • setBool
  • setFloat
  • setInteger
  • setDouble
  • setURL

Eine Sonderstellung bekommt folgende Methode:

  • setObject

Du rufst sie nach dem gleichen Schema auf:

Übergeben werden können hier aber nur Instanzen der folgender Klassen:

  • NSData
  • NSString
  • NSNumber
  • NSDate
  • NSArray
  • NSDictionary

Eine weitere Einschränkung gilt für NSArray und NSDictionary Objekte. Zulässig sind nur Property List Objekte. Mehr Informationen dazu findest Du im Property List Programming Guide von Apple.

Daten über NSUserDefaults lesen

Der lesende Zugriff erfolgt über die gleiche Klasse. Auch hier wird zunächst die Referenz auf ein Objekt geholt. Anschließend rufst Du eine dem Datentyp entsprechende Methode auf. Der übergibst Du den beim Speichern genutzt Schlüssel:

Der Beispielcode steht exemplarisch für die verschiedenen Methoden. Alle folgen dem gleichen Prinzip. Verfügbar sind:

  • arrayForKey
  • boolForKey
  • dataForKey
  • dictionaryForKey
  • floatForKey
  • stringArrayForKey
  • stringForKey
  • doubleForKey
  • URLForKey

Der Rückgabetyp erschließt sich über den Methodennamen. Bei boolForKey wird Dir Bool als Typ zurückgegeben. Über floatForKey wird Dir Float zurückgegeben und doubleForKey liefert einen Double-Wert. Eine Ausnahme ist unter anderem das Pendant zu setObject() – die Methode objectForKey:

Der Rückgabetyp ist AnyObject. Dieser besondere Datentyp ist ein Platzhalter, der für jedes Objekt steht. Das ermöglicht die Rückgabe verschiedener Datentypen, ist aber gleichzeitig auch eher ein Relikt aus Objective-C Zeiten. Die Objekte müssen daher wie im Beispiel mittels Casting in die eigentlichen Typen überführt werden.

Standard Rückgabewerte

Je nach Methode werden unterschiedliche Datentypen zurückgegeben. Welcher as ist kann weitestgehend am Namen abgeleitet werden. Wesentlich spannender ist das Verhalten, wenn der Schlüssel nicht gefunden wird. Es gibt keinen einheitlichen Optional, sondern unterschiedliche Ergebnisse. Unter Objective-C gab es nil nur bei Referenzen, nicht für primitive Datentypen. Stattdessen wird der negative Fall direkt über den Datentyp abgebildet.

Rückgabewerte:

  • integerForKey(): Int, wenn der Schlüssel nicht existiert 0
  • boolForKey(): Bool, wenn der Schlüssel nicht existiert false
  • floatForKey(): Float: wenn der Schlüssel nicht existiert 0.0
  • doubleForKey(): Double, wenn der Schlüssel nicht existiert 0.0
  • objectForKey(): AnyObject?

Das bedeutet mit anderen Worten, eine einfache Prüfung auf ungleich nil, z.B. mit guard, reicht nicht aus.

Best-Practices

Konstante Keys für den Zugriff verwenden

In den Beispielen oben habe ich bewusst auf jede zusätzliche Komplexität verzichtet. In der Praxis sollten aber die Schlüssel nicht manuell als String geschrieben werden. Das ist unübersichtlich. So ist auch nicht einfach nachvollziehbar, welche Keys über die Anwendung verteilt genutzt werden. Die Lösung sind Konstanten, entweder zentral oder in der jeweiligen Klasse:

Das hat mehrere Vorteile. Zum einen kannst Du den Key leichter ändern. Es gibt ja nur noch die eine Stelle. Aber gleichzeitig musst Du auch nicht alle Strings auswendig kennen. Jetzt kann die Autovervollständigung helfen. Das schützt auch gegen schnell übersehbare Fehler, die bei den einfachen String schnell gemacht werden. Aus „downloadCompleted“ wird schnell „downloadComplete“ (da fehlt ein „d“) und ähnliches.

Manuell synchronisieren?

Die NSUserDefaults wurden in der Vergangenheit nicht immer direkt gespeichert. Es konnten durchaus mehrere Sekunden vergehen, ehe die Daten persistent gespeichert wurden. Wenn das aber beim Beenden der App z.B. nicht passierte, konnten Daten verloren gehen. Es gibt in der Klasse NSUserDefaults eine Methode synchronize(), die genau dies verhindert. Der manuelle Aufruf hat also die Inhalte aus dem flüchtigen Speicher fest abgelegt. Seit iOS wird allerdings empfohlen darauf zu verzichten. Es ist nicht mehr notwendig. Im Gegenteil, synchronize wirkt sich negativ auf die Performance aus.

Keine Benutzerdaten hinterlegen

Du kannst in den NSUserDefaults theoretisch auch die eingegebenen Benutzerdaten für einen Account speichern. Das ist jedoch nicht zu empfehlen, denn die Daten sind unverschlüsselt auf dem System. Besser geeignet ist die Speicherung in der KeyChain. Das ist etwas mehr Aufwand, Deine Benutzer danken es Dir aber durch einen Gewinn an Datensicherheit.

Defaultwerte festlegen

Werden Einstellungen in der App vorgenommen, sind Standardwerte sehr nützlich. Statt überall zu prüfen ob der Key existiert und eine Alternative manuell zu setzen, kann die registerDefaults Methode der Klasse NSUserDefaults verwendet werden.

Es ist denkbar einfach. Es wird eine eine neue Property List erzeugt, zum Beispiel mit dem Namen Standardwerte.plist (New File -> iOS -> Resource -> Property List). In der Klasse AppDelegate kannst Du in der Methode application(_:didFinishLaunchingWithOptions) genau diese Datei auslesen und Deine Werte als Standardwerte festlegen:

In Deiner Datei Standardwerte.plist könntest Du so zum Beispiel für den Schlüssel „lastExecution“ aus dem Beispiel oben einen Startwert festlegen.

Preferences in der iCloud speichern?

Über die iCloud können die Einstellungen auch auf mehreren Geräten geteilt werden. Der verfügbare Key/Value Store kann über die Klasse NSUbiquitousKeyValueStore genutzt werden. Der Ansatz ist allerdings kein einfacher Ersatz für die NSUserDefaults. Nicht jeder hat den entsprechenden Account oder immer eine Netzwerkverbindung.

Apple empfiehlt daher die Einstellungen weiter lokal zu speichern und die Einstellungen in der Cloud zum Abgleich zu verwenden. Über die Notification NSUbiquitousKeyValueStoreDidChangeExternallyNotification kann die Änderung automatisch erkannt und verarbeitet werden. Weitere Informationen dazu findest Du im Preferences and Settings Programming Guide in der iOS Developer Library.

Foto: GotCredit by Got Credit, used under CC BY / Original used as background

[plusupgrade]

  • Artikel als PDF
  • Quellcode vom fertigen Projekt
  • Beispiel für iCloud Synchronisierung
  • NSUserDefaults Cheatsheet

[/plusupgrade]

[ds_preview]

Extras für PLUS-Mitglieder

Artikel als PDF

Cheatsheet

Das Beispielprojekt: Demo

Hier das Beispielprojekt in der Übersicht:

 

Beispielprojekt herunterladen

 

Schreib einen Kommentar

Your email address will not be published.