Jak zapisać dane lokalne w aplikacji Swift?

148

Obecnie pracuję nad aplikacją na iOS opracowaną w Swift i muszę przechowywać na urządzeniu niektóre treści utworzone przez użytkowników, ale nie mogę znaleźć prostego i szybkiego sposobu na przechowywanie / odbieranie treści użytkowników na urządzeniu.

Czy ktoś mógłby wyjaśnić, jak przechowywać i uzyskiwać dostęp do lokalnej pamięci masowej?

Chodzi o to, aby przechowywać dane, gdy użytkownik wykonuje akcję, i otrzymywać je po uruchomieniu aplikacji.

Nicklas Ridewing
źródło
Cześć, witaj, jakie dane przechowujesz? Czy możesz podać kod, który będzie przykładem tego, czego szukasz? Dzięki.
Wez
1
Dane, które muszę przechowywać, to w zasadzie tylko dane ciągów. Aby było to naprawdę proste, muszę zapisać dwie wartości String, które mogę otrzymać, gdy użytkownik ponownie uruchomi aplikację.
Nicklas Ridewing
2
Możesz użyć NSUserDefaults. Oto informacje, których potrzebujesz codingexplorer.com/nsuserdefaults-a-swift-introduction
bpolat

Odpowiedzi:

197

Najprostszym rozwiązaniem, jeśli przechowujesz tylko dwa ciągi, jest to NSUserDefaults, że w języku Swift 3 nazwa tej klasy została zmieniona na just UserDefaults.

Najlepiej przechowywać klucze w dowolnym miejscu na świecie, aby można było ich ponownie użyć w innym miejscu kodu.

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

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

W przypadku czegokolwiek poważniejszego niż drobna konfiguracja, flagi lub ciągi bazowe, powinieneś użyć jakiegoś trwałego magazynu - popularną opcją w tej chwili jest Realm, ale możesz również użyć własnych CoreData SQLite lub Apples .

Wez
źródło
7
Niezły przykład. Prawdopodobnie powinno to być jednak wyliczenie zamiast struktury.
pan-and-scan
3
To dziwna implementacja enum, jeśli masz tylko stałe statyczne, równie dobrze może to być struktura.
Craig Grummitt
1
Ten kod jest zły. Nie używaj metod KVC setValue(_:forKey:)do zapisywania danych w UserDefaults. Skorzystaj z podanych UserDefaultsmetod set(_:forKey:)(w Swift 3).
rmaddy
Zaktualizowałem kod Swift 3. Nie wiem, jaka powinna być składnia Swift 2.
rmaddy
@rmaddy jest w porządku, jak jest.
Wez
57

Mówią, że użyj NSUserDefaults

Kiedy po raz pierwszy wdrażałem długoterminowe (po zamknięciu aplikacji) przechowywanie danych, wszystko, co przeczytałem w Internecie, wskazywało na NSUserDefaults. Chciałem jednak przechowywać słownik i choć było to możliwe, okazywało się to uciążliwe. Spędziłem godziny, próbując usunąć błędy typu.

NSUserDefaults ma również ograniczoną funkcję

Dalsze czytanie ujawniło, jak odczyt / zapis NSUserDefaults naprawdę zmusza aplikację do odczytu / zapisu wszystkiego lub niczego naraz, więc nie jest to wydajne. Wtedy dowiedziałem się, że pobieranie tablicy nie jest proste. Zdałem sobie sprawę, że jeśli przechowujesz więcej niż kilka ciągów znaków lub wartości logicznych, NSUserDefaults naprawdę nie jest idealne.

Nie jest też skalowalne. Jeśli uczysz się kodować, naucz się skalowalnego sposobu. Używaj NSUserDefaults tylko do przechowywania prostych łańcuchów lub wartości logicznych związanych z preferencjami. Przechowuj tablice i inne dane za pomocą danych podstawowych, to nie jest tak trudne, jak mówią. Zacznij od czegoś małego.

Aktualizacja: Ponadto, jeśli dodasz obsługę Apple Watch, istnieje inny potencjalny czynnik. NSUserDefaults Twojej aplikacji jest teraz automatycznie wysyłane do rozszerzenia Watch.

Korzystanie z danych podstawowych

Zignorowałem więc ostrzeżenia, że ​​Core Data jest trudniejszym rozwiązaniem i zacząłem czytać. W ciągu trzech godzin sprawiłem, że zadziałało. Moja tablica tabel została zapisana w danych podstawowych i ponownie załadowana po otwarciu kopii zapasowej aplikacji! Kod samouczka był dość łatwy do zaadaptowania i mogłem przechowywać w nim tablice tytułów i szczegółów z niewielkim dodatkowym eksperymentowaniem.

Dlatego każdy, kto czyta ten post, boryka się z problemami związanymi z typem NSUserDefault lub potrzebuje czegoś więcej niż przechowywanie ciągów znaków, może rozważyć spędzenie godziny lub dwóch na zabawie z podstawowymi danymi.

Oto samouczek, który przeczytałem:

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

Jeśli nie zaznaczyłeś „Dane podstawowe”

Jeśli nie zaznaczyłeś opcji „Dane podstawowe” podczas tworzenia aplikacji, możesz dodać ją później i zajmie to tylko pięć minut:

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

Jak usunąć z list podstawowych danych

Usuń dane z Coredata Swift

Dave G.
źródło
6
Istnieje pośrednia podstawa zapisywania danych w pliku .plist gdzieś w katalogu Dokumenty lub w innym miejscu w katalogu piaskownicy aplikacji.
Nicolas Miari
Jestem nowicjuszem, więc podejmij to pytanie z przymrużeniem oka, ale czy to kompromis? NSUserDefaults i tak jest praktycznie plikiem p.list, więc (na podstawie tego, co przeczytałem) zapisywanie do pliku p.list w katalogu dokumentów jest mniej więcej tym samym, co używanie NSUserDefaults. Powodem, dla którego nie użyłem tej metody, było to, że zapisywanie tablicy do p.list brzmiało tak, jakby wymagało dodatkowych kroków, takich jak konwersja na inny typ, a później, podczas przywracania wartości, wymagało ponownego przypisania wartości po ich odczytaniu z listy p. Ponownie, wyłącznie w oparciu o moje odczyty z ostatnich kilku dni.
Dave G
Cóż, zapisanie tablicy w pliku .plist jest całkiem proste (wszystkie obiekty w hierarchii to tablice, ciągi znaków, słowniki lub liczby (bez klas niestandardowych).
Nicolas Miari
Zgoda, przeczytanie pliku to wszystko albo nic, ale możesz podzielić dane na osobne pliki w zależności od ich struktury. A CoreData miał znaczną krzywą uczenia się, przynajmniej kiedy ostatnio sprawdzałem.
Nicolas Miari
3
Powiedziałbym: użyj NSUserDefaults do przechowywania preferencji (to w końcu oznacza nazwa), pęku kluczy do przechowywania trwałych i / lub wrażliwych danych, niestandardowego schematu plików .plist dla bardzo podstawowych danych generowanych przez aplikację i CoreData dla cięższych rzeczy .
Nicolas Miari
11

Okey, więc dzięki @bploatacji i linkowi do http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/

Przekonałem się, że odpowiedź jest dość prosta w przypadku podstawowego przechowywania ciągów.

let defaults = NSUserDefaults.standardUserDefaults()

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

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

Podsumowałem to tutaj http://ridewing.se/blog/save-local-data-in-swift/

Nicklas Ridewing
źródło
9

Używanie NSCoding i NSKeyedArchiver to kolejna świetna opcja dla danych, które są zbyt złożone NSUserDefaults, ale dla których CoreData byłoby przesadą. Daje również możliwość bardziej jawnego zarządzania strukturą plików, co jest świetne, jeśli chcesz używać szyfrowania.

Patrick Lynch
źródło
7

W przypadku Swift 4.0 stało się to łatwiejsze:

let defaults = UserDefaults.standard
//Set
defaults.set(passwordTextField.text, forKey: "Password")
//Get
let myPassword = defaults.string(forKey: "Password")
LevinsonTechnologies
źródło
6

Swift 3.0

Setter: pamięć lokalna

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

 defaults.synchronize()

Getter: pamięć lokalna

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

//perform your task on success }
Ravindra Shekhawat
źródło
2
Nie powinieneś zapisywać czegoś takiego jak authtoken w UserDefaults ... Użyj do tego pęku kluczy
BilalReffas
5

Dla Swift 3

UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")
Arun Yokesh
źródło
Nie używaj metod KVC do zapisywania danych.
rmaddy
4

Dla kogoś, kto z jakiegoś powodu nie chciałby obsługiwać UserDefaults, istnieje inna opcja - NSKeyedArchiver i NSKeyedUnarchiver. Pomaga zapisywać obiekty do pliku za pomocą archiwizatora i ładować zarchiwizowany plik do oryginalnych obiektów.

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

Napisałem proste narzędzie do zapisywania / ładowania obiektów w lokalnej pamięci, użyłem przykładowych kodów powyżej. Może zechcesz to zobaczyć. https://github.com/DragonCherry/LocalStorage

DragonCherry
źródło
4

Swift 5+

Żadna z odpowiedzi tak naprawdę nie obejmuje szczegółowo domyślnych wbudowanych funkcji lokalnej pamięci masowej. Potrafi znacznie więcej niż tylko struny.

Dostępne są następujące opcje pochodzące bezpośrednio z dokumentacji Apple dotyczące „pobierania” danych z ustawień domyślnych.

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.

Oto opcje „ustawienia”

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.

Jeśli przechowujesz takie rzeczy, jak preferencje, a nie duży zestaw danych, są to doskonałe opcje.

Podwójny przykład :

Oprawa:

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

Pierwsze:

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

Co ciekawe w jednym z getterów jest DictionaryRepresentation , ten poręczny getter weźmie wszystkie typy danych, niezależnie od tego, czym one są i umieści je w ładnym słowniku, do którego można uzyskać dostęp za pomocą nazwy ciągu i poda poprawny odpowiedni typ danych, gdy o to poprosisz to z powrotem, ponieważ jest typu „any” .

Można przechowywać własnych klas i obiektów, również za pomocą func set(Any?, forKey: String)i func object(forKey: String) -> Any?setter i getter odpowiednio.

Mam nadzieję, że to wyjaśnia więcej możliwości klasy UserDefaults do przechowywania danych lokalnych.

Pamiętając o tym, ile należy przechowywać i jak często, Hardy_Germany udzielił dobrej odpowiedzi na to w tym poście , oto cytat z tego

Jak wielu już wspomniało: nie jestem świadomy żadnych ograniczeń SIZE (poza pamięcią fizyczną) do przechowywania danych w .plist (np. UserDefaults). Więc to nie jest kwestia ILE.

Prawdziwe pytanie powinno brzmieć JAK CZĘSTO wpisujesz nowe / zmienione wartości ... A to jest związane z wyczerpaniem baterii, które spowoduje to zapisywanie.

IOS nie ma szans na uniknięcie fizycznego zapisu na „dysku” w przypadku zmiany jednej wartości, tylko po to, aby zachować integralność danych. W przypadku opcji UserDefaults powoduje to przepisanie całego pliku na dysk.

Powoduje to włączenie „dysku” i przedłużenie jego zasilania, a także zapobiega przejściu systemu IOS do stanu niskiego poboru mocy.

Coś jeszcze, o czym wspomniał użytkownik Mohammad Reza Farahani z tego postu, to asynchroniczny i synchronicznyInną rzeczą wartą charakter userDefaults.

Po ustawieniu wartości domyślnej jest ona zmieniana synchronicznie w ramach procesu i asynchronicznie na trwały magazyn i inne procesy.

Na przykład, jeśli zapiszesz i szybko zamkniesz program, możesz zauważyć, że nie zapisuje on wyników, jest tak, ponieważ utrzymuje się asynchronicznie. Możesz nie zauważyć tego przez cały czas, więc jeśli planujesz oszczędzać przed zamknięciem programu, możesz to uwzględnić, dając mu trochę czasu na zakończenie.

Może ktoś ma na to fajne rozwiązania, którymi mógłby się podzielić w komentarzach?

Joseph Astrahan
źródło
0

NsUserDefaults zapisuje tylko małe rozmiary zmiennych. Jeśli chcesz zapisać wiele obiektów, możesz użyć CoreData jako rozwiązania natywnego lub stworzyłem bibliotekę, która pomaga ci zapisywać obiekty tak łatwo, jak funkcja .save (). Opiera się na SQLite.

SundeedQLite

Sprawdź to i powiedz mi swoje komentarze

nour sandid
źródło