如何在 Swift 应用程序中保存本地数据?

How to save local data in a Swift app?

我目前正在开发 iOS 在 Swift 开发的应用程序,我需要在设备上存储一些用户创建的内容,但我似乎找不到简单快捷的方法store/receive 设备上用户内容的方式。

有人可以解释一下如何存储和访问本地存储吗?

想法是在用户执行操作时存储数据,并在应用启动时接收数据。

存储几个字符串或常见类型的最简单解决方案是UserDefaults

The UserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Boolean values, and URLs.

UserDefaults 让我们根据我们选择的密钥存储对象,将这些密钥存储在可访问的地方是个好主意,这样我们就可以重用它们。

按键

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

设置

let defaults = UserDefaults.standard
defaults.set("Some String Value", forKey: DefaultsKeys.keyOne)
defaults.set("Another String Value", forKey: DefaultsKeys.keyTwo)

得到

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

在 Swift 2.0 中 UserDefaults 被称为 NSUserDefaults 并且 setter 和 getter 的命名略有不同:

设置

let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("Some String Value", forKey: DefaultsKeys.keyOne)
defaults.setObject("Another String Value", forKey: DefaultsKeys.keyTwo)

得到

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
}

对于比次要配置更严重的任何事情,您应该考虑使用更强大的持久存储:

好的,谢谢 @bploat and the link to 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/

他们说使用 NSUserDefaults

当我第一次实现长期(应用程序关闭后)数据存储时,我在网上阅读的所有内容都指向 NSUserDefaults。然而,我想存储一本字典,尽管可能,但事实证明这很麻烦。我花了几个小时试图让 type-errors 离开。

NSUserDefaults 在功能上也受到限制

进一步阅读揭示了 NSUserDefaults 的 read/write 如何真正强制应用程序一次全部 read/write 一切或什么都不做,因此效率不高。然后我了解到检索数组并不是直截了当的。我意识到如果要存储多个字符串或布尔值,NSUserDefaults 确实不理想。

它也不可扩展。如果您正在学习如何编码,请学习可扩展的方式。仅使用 NSUserDefaults 来存储与首选项相关的简单字符串或布尔值。使用 Core Data 存储数组和其他数据,并不像他们说的那么难。从小做起。

更新:此外,如果您添加 Apple Watch 支持,还有另一个潜在的考虑因素。您应用的 NSUserDefaults 现在会自动发送到 Watch Extension。

使用核心数据

所以我忽略了关于核心数据是一个更困难的解决方案的警告并开始阅读。不到三个小时,我就让它工作了。我将 table 数组保存在核心数据中,并在打开应用程序备份时重新加载数据!教程代码很容易适应,我只需进行一些额外的实验就可以让它同时存储标题和详细信息数组。

因此,对于正在为 NSUserDefault 类型问题苦苦挣扎或需要的不仅仅是存储字符串的阅读本文的 post 的任何人,请考虑花一两个小时来研究核心数据。

这是我阅读的教程:

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

如果你没勾选"Core Data"

如果您在创建应用时没有勾选"Core Data",您可以在之后添加它,只需五分钟:

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

如何从核心数据列表中删除

Delete Data from Coredata Swift

对于 NSUserDefaults 来说过于复杂的数据,使用 NSCoding and NSKeyedArchiver 是另一个很好的选择,但 CoreData 对它来说就有点过分了。它还使您有机会更明确地管理文件结构,如果您想使用加密,这非常好。

对于Swift 3

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

Swift 3.0

Setter:本地存储

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

 defaults.synchronize()

Getter:本地存储

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

//perform your task on success }

对于出于某些原因不喜欢处理 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)
}

我已经为本地存储中的 save/load 个对象编写了一个简单的实用程序,使用了上面的示例代码。你可能想看看这个。 https://github.com/DragonCherry/LocalStorage

对于 Swift 4.0,这变得更容易了:

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

NsUserDefaults 仅保存较小的变量大小。 如果你想保存许多对象,你可以使用 CoreData 作为本机解决方案,或者我创建了一个库来帮助你像 .save() 函数一样简单地保存对象。它基于 SQLite。

SundeedQLite

查看并告诉我您的意见

Swift 5+

None 的答案确实详细介绍了默认内置的本地存储功能。它可以做的远不止是字符串。

您可以直接从 Apple 文档中获得以下选项,以获得来自默认值的 'getting' 数据。

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.

以下是 'setting'

的选项
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")

其中一个 getter 的有趣之处在于 dictionaryRepresentation,这个方便的 getter 将获取您所有的数据类型,无论它们是什么并放入它们变成一个很好的字典,你可以通过它的字符串名称访问它,并在你要求它时给出正确的相应数据类型,因为它的类型是 'any'.

您也可以相应地使用 func set(Any?, forKey: String)func object(forKey: String) -> Any? setter 和 getter 存储您自己的 classes 和对象。

希望这能进一步说明 UserDefaults class 在存储本地数据方面的强大功能。

关于应该存储多少和多久存储一次,Hardy_Germany 对此 post 给出了很好的答案,这里是引用它

As many already mentioned: I'm not aware of any SIZE limitation (except physical memory) to store data in a .plist (e.g. UserDefaults). So it's not a question of HOW MUCH.

The real question should be HOW OFTEN you write new / changed values... And this is related to the battery drain this writes will cause.

IOS has no chance to avoid a physical write to "disk" if a single value changed, just to keep data integrity. Regarding UserDefaults this cause the whole file rewritten to disk.

This powers up the "disk" and keep it powered up for a longer time and prevent IOS to go to low power state.

正如用户 Mohammad Reza Farahani 在这个 中提到的,其他需要注意的是 异步同步 的性质userDefaults.

When you set a default value, it’s changed synchronously within your process, and asynchronously to persistent storage and other processes.

例如,如果您保存并快速关闭程序,您可能会注意到它没有保存结果,这是因为它是异步持久化的。你可能不会一直注意到这一点,所以如果你计划在退出程序之前保存,你可能需要给它一些时间来解决这个问题。

也许有人对此有一些不错的解决方案,他们可以在评论中分享?

这很好地解释了如何在 Swift 5 中执行此操作:https://www.hackingwithswift.com/example-code/system/how-to-save-user-settings-using-userdefaults

总结:

要设置一个值:

let defaults = UserDefaults.standard
defaults.set("value", forKey: "key")

获取字符串值:

let key = defaults.object(forKey: "StringKey") as? [String] ?? [String]()

获取整数值:

let key = defaults.integer(forKey: "IntegerKey")