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


148

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

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

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


嘿,欢迎您,您要存储哪种数据?您可以提供任何代码来举例说明您正在寻找什么吗?谢谢。
Wez 2015年

1
我需要存储的数据基本上只是字符串数据。因此,为使其简单起见,我需要保存两个String值,以供用户重新启动应用程序时接收。
Nicklas Ridewing 2015年

2
您可以使用NSUserDefaults。这是您需要的信息 codingexplorer.com/nsuserdefaults-a-swift-introduction
bpolat 2015年

Answers:


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
}

雨燕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
那是枚举的一个奇怪实现,如果您只有静态常量,那么它也可能是一个结构。
Craig Grummitt '16

1
该代码是错误的。不要使用KVC方法setValue(_:forKey:)将数据保存到UserDefaults。使用(在Swift 3中)提供的UserDefaults方法set(_:forKey:)
rmaddy

我更新了Swift 3代码。我不知道Swift 2语法应该是什么。
rmaddy

@rmaddy没关系。
Wez

57

他们说使用NSUserDefaults

当我第一次实现长期(应用关闭后)数据存储时,我在网上阅读的所有内容都指向NSUserDefaults。但是,我想存储一个字典,尽管可能,但事实证明这很痛苦。我花了几个小时试图消除类型错误。

NSUserDefaults也受功能限制

进一步的阅读显示了NSUserDefaults的读/写如何真正迫使应用程序一次读/写所有内容或什么都不做,因此效率不高。然后我了解到检索数组不是直接的。我意识到,如果要存储多个字符串或布尔值,NSUserDefaults确实不是理想的选择。

它也是不可扩展的。如果您要学习编码,请学习可扩展方式。仅将NSUserDefaults用于存储与首选项相关的简单字符串或布尔值。使用Core Data存储阵列和其他数据,并不像他们所说的那么难。从小开始。

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

使用核心数据

因此,我忽略了有关核心数据是更困难的解决方案的警告,并开始阅读。三个小时之内,我就开始工作了。我将表数组保存在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
将数据保存到Documents目录中某个位置或应用程序沙箱目录中其他位置的.plist文件是有中间立场的。
Nicolas Miari

我是新手,请带着一粒盐来回答这个问题,但这是中间立场吗?无论如何,NSUserDefaults实际上是一个p.list文件,因此(根据我的阅读)保存到documents目录中的p.list文件与使用NSUserDefaults有点类似。我之所以不使用该方法,是因为将数组保存到p.list听起来像它涉及额外的步骤,例如将其转换为另一种类型,然后在还原值时,需要在读取它们后重新分配值从p.list。同样,仅基于我过去几天的阅读量。
Dave G

好吧,将数组保存到.plist文件非常简单(层次结构中的所有对象都是数组,字符串,字典或数字(无自定义类)。)
Nicolas Miari 2015年

同意读取文件是全有还是全无,但是您可以根据其结构将数据拆分为单独的文件。而且,至少在我上次检查时,CoreData的学习曲线很明显。
Nicolas Miari

3
我会说:使用NSUserDefaults存储首选项(毕竟这就是名称的含义),用于存储持久性和/或敏感数据的钥匙串,用于非常基本的应用程序生成的数据的.plist文件的自定义方案,以及用于处理更繁重内容的CoreData 。
Nicolas Miari

11

好的,感谢@bploathttp://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")

6

斯威夫特3.0

设置器:本地存储

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 }

2
您不应该在UserDefaults中保存像authtoken这样的内容。请为此使用钥匙串
BilalReffas


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


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")

其中一个有趣的getter方法是dictionaryRepresentation,这个方便的getter方法将获取所有数据类型,无论它们是什么,并将它们放入一个漂亮的字典中,您可以通过其字符串名称进行访问,并在需要时提供正确的对应数据类型它返回,因为它是'any'类型

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

希望这可以进一步阐明UserDefaults类用于存储本地数据的功能。

就说明你应该多少存储和频率,Hardy_Germany介绍了,在这个好答案,这里是从它报价

正如已经提到的许多:我不知道将数据存储在.plist中的任何SIZE限制(物理内存除外)(例如UserDefaults)。因此,这不是如何的问题。

真正的问题应该是您如何经常编写新的/更改的值...这与写入将导致的电池消耗有关。

如果更改单个值,IOS就没有机会避免物理写入“磁盘”,只是为了保持数据完整性。关于UserDefaults,这将导致整个文件重写到磁盘。

这会为“磁盘”加电,并使其保持更长的供电时间,并防止IOS进入低功耗状态。

用户Mohammad Reza Farahani在本文中提到的其他要注意是userDefaults 的异步同步性质。

设置默认值时,它将在您的流程中同步更改,并异步更改为持久性存储和其他流程。

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

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


0

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

SundeedQLite

查看并告诉我您的评论

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.