Как сохранить локальные данные в приложении Swift?

149

В настоящее время я работаю над приложением для iOS, разработанным в Swift, и мне нужно хранить какое-то пользовательское содержимое на устройстве, но я не могу найти простой и быстрый способ хранения / получения пользовательского контента на устройстве.

Может ли кто-нибудь объяснить, как хранить и получать доступ к локальному хранилищу?

Идея состоит в том, чтобы хранить данные, когда пользователь выполняет действие, и получать их при запуске приложения.

Никлас Райдингвинг
источник
Привет и добро пожаловать, какие данные вы храните? Можете ли вы предоставить какой-либо код, чтобы привести пример того, что вы ищете? Спасибо.
Wez
1
Данные, которые мне нужно хранить, в основном просто строковые данные. Поэтому для простоты мне нужно сохранить два значения String, которые я смогу получить, если пользователь перезапустит приложение.
Никлас Ридингинг
2
Вы можете использовать NSUserDefaults. Вот информация, которая вам нужна codingexplorer.com/nsuserdefaults-a-swift-introduction
bpolat

Ответы:

197

Самое простое решение, если вы просто храните две строки, это то NSUserDefaults, что в Swift 3 этот класс был переименован в just UserDefaults.

Лучше всего хранить свои ключи где-нибудь на глобальном уровне, чтобы вы могли использовать их в другом месте своего кода.

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
}

Для чего-либо более серьезного, чем второстепенные конфигурации, флаги или базовые строки, вы должны использовать какое-то постоянное хранилище. На данный момент популярным вариантом является Realm, но вы также можете использовать SQLite или Apple, имеющие собственные CoreData .

Мы з
источник
7
Хороший пример. Это, вероятно, должно быть перечислением вместо структуры, хотя.
панорамирование и сканирование
3
Это странная реализация enum, если у вас есть только статические константы, это может быть и структура.
Крейг Груммитт,
1
Этот код неверен. Не используйте методы KVC setValue(_:forKey:)для сохранения данных в UserDefaults. Используйте предоставленные UserDefaultsметоды set(_:forKey:)(в Swift 3).
rmaddy
Я обновил код Swift 3. Я не знаю, каким должен быть синтаксис Swift 2.
rmaddy
@rmaddy это нормально, как есть.
Wez
57

Они говорят, что используют NSUserDefaults

Когда я впервые реализовывал долгосрочное (после закрытия приложения) хранилище данных, все, что я читал в Интернете, указывало на NSUserDefaults. Тем не менее, я хотел сохранить словарь и, хотя это было возможно, это было больно. Я часами пытался заставить ошибки типа исчезнуть.

NSUserDefaults также ограничен в функции

Дальнейшее чтение показало, как чтение / запись NSUserDefaults действительно заставляет приложение читать / записывать все или ничего, все сразу, так что это не эффективно. Затем я узнал, что получение массива не так просто. Я понял, что если вы храните больше нескольких строк или логических значений, NSUserDefaults на самом деле не идеален.

Это также не масштабируется. Если вы учитесь кодировать, изучите масштабируемый способ. Используйте NSUserDefaults только для хранения простых строк или логических значений, связанных с настройками. Хранить массивы и другие данные с помощью Core Data, это не так сложно, как говорят. Просто начните с малого.

Обновление: также, если вы добавите поддержку Apple Watch, есть еще один потенциальный фактор. NSUserDefaults вашего приложения теперь автоматически отправляется на расширение Watch.

Использование основных данных

Поэтому я проигнорировал предупреждения о том, что Core Data является более сложным решением, и начал читать. Через три часа у меня все заработало. У меня был массив таблиц, сохраненный в Core Data и перезагружающий данные при открытии приложения! Код учебника был достаточно прост в адаптации, и я смог сохранить в нем как заголовки, так и подробные массивы, лишь немного поэкспериментировав.

Поэтому для тех, кто читает этот пост, кто сталкивается с проблемами типа NSUserDefault или нуждается в чем-то большем, чем хранение строк, рассмотрите возможность потратить час или два, играя с основными данными.

Вот учебник, который я прочитал:

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

Если вы не отметили «Основные данные»

Если вы не отметили «Основные данные» при создании приложения, вы можете добавить его позже, и это займет всего пять минут:

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

Как удалить из основных списков данных

Удалить данные из Coredata Swift

Дэйв Дж
источник
6
Существует середина сохранения ваших данных в файл .plist где-нибудь в каталоге Documents или в другом месте в каталоге песочницы приложения.
Николас Миари
Я новичок, так что ответьте на этот вопрос с небольшим количеством соли, но это золотая середина? В любом случае NSUserDefaults - это практически файл p.list, поэтому (основываясь на том, что я прочитал) сохранение в файл p.list в каталоге документов похоже на использование NSUserDefaults. Причина, по которой я не использовал этот метод, заключалась в том, что сохранение массива в список p.list звучало так, как будто оно требовало дополнительных шагов, таких как преобразование его в другой тип, а затем, при восстановлении значений, требовалось переназначить значения после их чтения. из списка Опять же, исключительно на основе моего чтения за последние несколько дней.
Дейв Г
Что ж, сохранить массив в файл .plist довольно просто (все объекты в иерархии - это массивы, строки, словари или числа (без пользовательских классов).
Николас Миари
Согласитесь, чтение файла - это все или ничего, но вы можете разделить ваши данные на отдельные файлы в зависимости от его структуры. И у CoreData была значительная кривая обучения, по крайней мере, в последний раз, когда я проверял.
Николас Миари
3
Я бы сказал: используйте NSUserDefaults для хранения настроек (в конце концов, это и означает имя), цепочку для ключей для хранения постоянных и / или конфиденциальных данных, вашу собственную схему файлов .plist для самых базовых данных, генерируемых приложением, и CoreData для более тяжелых вещей ,
Николас Миари
11

Хорошо, благодаря @bploat и ссылке на http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/

Я обнаружил, что ответ довольно прост для некоторого базового хранения строк.

let defaults = NSUserDefaults.standardUserDefaults()

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

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

Я суммировал это здесь http://ridewing.se/blog/save-local-data-in-swift/

Никлас Райдингвинг
источник
9

Использование NSCoding и NSKeyedArchiver - еще один отличный вариант для данных, которые слишком сложны NSUserDefaults, но для которых CoreData будет излишним. Это также дает вам возможность более четко управлять структурой файлов, что прекрасно, если вы хотите использовать шифрование.

Патрик Линч
источник
7

Для Swift 4.0 это стало проще:

let defaults = UserDefaults.standard
//Set
defaults.set(passwordTextField.text, forKey: "Password")
//Get
let myPassword = defaults.string(forKey: "Password")
LevinsonTechnologies
источник
6

Swift 3.0

Сеттер: Локальное хранилище

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

 defaults.synchronize()

Получатель: Локальное хранилище

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

//perform your task on success }
Равиндра Шехават
источник
2
Вы не должны сохранять что-то, как authtoken в UserDefaults .. Пожалуйста, используйте цепочку для ключей для этого
BilalReffas
5

Для Swift 3

UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")
Арун Йокеш
источник
Не используйте методы KVC для сохранения данных.
rmaddy
4

Для тех, кто по каким-либо причинам не предпочел бы обрабатывать UserDefaults, есть другой вариант - NSKeyedArchiver & NSKeyedUnarchiver. Это помогает сохранять объекты в файл с помощью архиватора и загружать архивированный файл в исходные объекты.

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

Я написал простую утилиту для сохранения / загрузки объектов в локальном хранилище, использовали примеры кода выше. Возможно, вы захотите увидеть это. https://github.com/DragonCherry/LocalStorage

DragonCherry
источник
4

Свифт 5+

Ни один из ответов на самом деле подробно не описывает встроенные возможности локального хранилища по умолчанию. Это может сделать гораздо больше, чем просто строки.

У вас есть следующие опции прямо из документации Apple для «получения» данных по умолчанию.

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.

Вот варианты «настройки»

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.

Если вы храните такие вещи, как настройки, а не большой набор данных, это идеальный вариант.

Двойной пример :

Установка:

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

Получение:

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

Что интересно в одном из методов получения, так это словарь dictionaryRepresentation , этот удобный метод получения возьмет все ваши типы данных независимо от того, какие они есть, и поместит их в хороший словарь, к которому вы можете получить доступ по его имени строки, и даст правильный соответствующий тип данных, когда вы запрашиваете это назад, так как это типа «любой» .

Вы можете хранить свои собственные классы и объекты, также используя func set(Any?, forKey: String)и func object(forKey: String) -> Any?setter и getter соответственно.

Надеюсь, что это проясняет возможности класса UserDefaults для хранения локальных данных.

На заметку о том, сколько вы должны хранить и как часто, Hardy_Germany дал хороший ответ на этот вопрос в этом посте , вот цитата из него

Как уже упоминалось, я не знаю каких-либо ограничений размера (кроме физической памяти) для хранения данных в .plist (например, UserDefaults). Так что это не вопрос, сколько.

Реальный вопрос должен заключаться в том, КАК ЧАСТО вы пишете новые / измененные значения ... И это связано с разрядкой батареи, которая вызывает эти записи.

У IOS нет шансов избежать физической записи на «диск» при изменении одного значения, просто чтобы сохранить целостность данных. Относительно UserDefaults это вызывает перезапись всего файла на диск.

Это включает «диск» и поддерживает его в течение более длительного времени и предотвращает переход IOS в режим низкого энергопотребления.

Еще одно замечание, упомянутое пользователем Мохаммадом Реза Фарахани из этого поста, - это асинхронный и синхронный характер userDefaults.

Когда вы устанавливаете значение по умолчанию, оно изменяется синхронно внутри вашего процесса и асинхронно с постоянным хранилищем и другими процессами.

Например, если вы сохраняете и быстро закрываете программу, вы можете заметить, что она не сохраняет результаты, это потому, что она сохраняется асинхронно. Вы можете не замечать этого все время, поэтому, если вы планируете сохранить перед выходом из программы, вы можете отнести это к учету, предоставив ему некоторое время для завершения.

Может быть, у кого-то есть хорошие решения для этого, которыми они могут поделиться в комментариях?

Иосиф Астрахань
источник
0

NsUserDefaults сохраняет только небольшие переменные размеры. Если вы хотите сохранить много объектов, вы можете использовать CoreData как собственное решение, или я создал библиотеку, которая поможет вам сохранять объекты так же просто, как функция .save (). Он основан на SQLite.

SundeedQLite

Проверьте это и скажите мне свои комментарии

Nour Sandid
источник