ios Swift 3 从 userDefaults 保存和检索自定义对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37980432/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Swift 3 saving and retrieving custom object from userDefaults
提问by user773881
I have this in Playground using Swift 3, Xcode 8.0:
我在 Playground 中使用 Swift 3, Xcode 8.0:
import Foundation
class Person: NSObject, NSCoding {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
required convenience init(coder aDecoder: NSCoder) {
let name = aDecoder.decodeObject(forKey: "name") as! String
let age = aDecoder.decodeObject(forKey: "age") as! Int
self.init(
name: name,
age: age
)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(age, forKey: "age")
}
}
create array of Person
创建 Person 数组
let newPerson = Person(name: "Joe", age: 10)
var people = [Person]()
people.append(newPerson)
encode the array
对数组进行编码
let encodedData = NSKeyedArchiver.archivedData(withRootObject: people)
print("encodedData: \(encodedData))")
save to userDefaults
保存到用户默认值
let userDefaults: UserDefaults = UserDefaults.standard()
userDefaults.set(encodedData, forKey: "people")
userDefaults.synchronize()
check
查看
print("saved object: \(userDefaults.object(forKey: "people"))")
retreive from userDefaults
从 userDefaults 中检索
if let data = userDefaults.object(forKey: "people") {
let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: data as! Data)
print("myPeopleList: \(myPeopleList)")
}else{
print("There is an issue")
}
just check the archived data
只需检查存档数据
if let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: encodedData){
print("myPeopleList: \(myPeopleList)")
}else{
print("There is an issue")
}
I'm not able to correctly save the data object to userDefaults, and in addition, the check at the bottom creates the error "fatal error: unexpectedly found nil while unwrapping an Optional value". The "check" line also shows the saved object is nil. Is this an error in my object's NSCoder?
我无法将数据对象正确保存到 userDefaults,此外,底部的检查会产生错误“致命错误:在解开可选值时意外发现 nil”。“检查”行还显示保存的对象为零。这是我对象的 NSCoder 中的错误吗?
回答by Leo Dabus
Swift 4 Note
斯威夫特 4 笔记
You can once again save/test your values in a Playground
您可以再次在 Playground 中保存/测试您的值
Swift 3
斯威夫特 3
UserDefaults need to be tested in a real project. Note: No need to force synchronize. If you want to test the coding/decoding in a playground you can save the data to a plist file in the document directory using the keyed archiver. You need also to fix some issues in your class:
UserDefaults 需要在实际项目中进行测试。注意:无需强制同步。如果您想在操场上测试编码/解码,您可以使用密钥存档器将数据保存到文档目录中的 plist 文件中。您还需要解决班级中的一些问题:
class Person: NSObject, NSCoding {
let name: String
let age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
required init(coder decoder: NSCoder) {
self.name = decoder.decodeObject(forKey: "name") as? String ?? ""
self.age = decoder.decodeInteger(forKey: "age")
}
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
coder.encode(age, forKey: "age")
}
}
Testing:
测试:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// setting a value for a key
let newPerson = Person(name: "Joe", age: 10)
var people = [Person]()
people.append(newPerson)
let encodedData = NSKeyedArchiver.archivedData(withRootObject: people)
UserDefaults.standard.set(encodedData, forKey: "people")
// retrieving a value for a key
if let data = UserDefaults.standard.data(forKey: "people"),
let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Person] {
myPeopleList.forEach({print( let age = aDecoder.decodeObject(forKey: "age") as! Int
.name, let age = aDecoder.decodeInteger(forKey: "age")
.age)}) // Joe 10
} else {
print("There is an issue")
}
}
}
回答by ibuprofane
let myBool = aDecoder.decodeBoolean(forKey: "myStoredBool")
let myFloat = aDecoder.decodeFloat(forKey: "myStoredFloat")
This has been changed for Swift 3; this no longer works for value types. The correct syntax is now:
这已在 Swift 3 中更改;这不再适用于值类型。正确的语法现在是:
self.age = aDecoder.decodeObject(forKey: "age") as? Int ?? aDecoder.decodeInteger(forKey: "age")
There are associated decode...() functions for various different types:
有各种不同类型的相关 decode...() 函数:
extension UserDefaults {
func save<T:Encodable>(customObject object: T, inKey key: String) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(object) {
self.set(encoded, forKey: key)
}
}
func retrieve<T:Decodable>(object type:T.Type, fromKey key: String) -> T? {
if let data = self.data(forKey: key) {
let decoder = JSONDecoder()
if let object = try? decoder.decode(type, from: data) {
return object
}else {
print("Couldnt decode object")
return nil
}
}else {
print("Couldnt find key")
return nil
}
}
}
Edit: Full list of all possible decodeXXX functions in Swift 3
编辑:Swift 3 中所有可能的 decodeXXX 函数的完整列表
Edit:
编辑:
Another important note: If you have previously saved data that was encoded with an older version of Swift, those values mustbe decoded using decodeObject(), howeveronce you re-encode the data using encode(...) it can no longer be decoded with decodeObject() if it's a value type. Therefore Markus Wyss's answer will allow you to handle the case where the data was encoded using either Swift version:
另一个重要注意事项:如果您之前保存了使用旧版本 Swift 编码的数据,则必须使用 decodeObject()对这些值进行解码,但是一旦您使用 encode(...) 重新编码数据,就不能再使用如果它是值类型,则使用 decodeObject() 解码。因此,Markus Wyss 的回答将允许您处理使用 Swift 版本编码数据的情况:
class UpdateProfile: Codable {
//Your stuffs
}
回答by Bishow Gurung
In Swift 4:
在 Swift 4 中:
You can use Codable to save and retrieve custom object from the Userdefaults. If you're doing it frequently then you can add as extension and use it like below.
您可以使用 Codable 从 Userdefaults 中保存和检索自定义对象。如果您经常这样做,那么您可以添加为扩展名并像下面一样使用它。
let updateProfile = UpdateProfile()
//To save the object
UserDefaults.standard.save(customObject: updateProfile, inKey: "YourKey")
//To retrieve the saved object
let obj = UserDefaults.standard.retrieve(object: UpdateProfile.self, fromKey: "YourKey")
Your Class must follow Codable. Its just a typealias for both Encodable & Decodable Protocol.
您的班级必须遵循 Codable。它只是可编码和可解码协议的类型别名。
self.age = aDecoder.decodeObject(forKey: "age") as? Int ?? aDecoder.decodeInteger(forKey: "age")
Usage:
用法:
required init(coder decoder: NSCoder) // NOT REQUIRED ANY MORE, DECODABLE TAKES CARE OF IT
func encode(with coder: NSCoder) // NOT REQUIRED ANY MORE, ENCODABLE TAKES CARE OF IT
For more Encoding and Decoding Custom types, Please go through the Apple's documentation.
有关更多编码和解码自定义类型,请阅读Apple 的文档。
回答by mswyss
Try this:
尝试这个:
struct Person : Codable {
var name:String
}
OR
class Person : Codable {
var name:String
init(name:String) {
self.name = name
}
}
回答by Dhaval H. Nena
A simple example of saving "Custom Object" in UserDefaults would be as below :
在 UserDefaults 中保存“自定义对象”的简单示例如下:
You do not need to write the boilerplate code for saving/retrieving objects with the help of THE GREAT 'CODABLE', that's what it is there for you to rescue from irritating manual encoding/decoding.
在伟大的“可编码”的帮助下,您无需编写用于保存/检索对象的样板代码,这就是您可以从烦人的手动编码/解码中解救出来的东西。
So just get rid of below two methods from your code if you're already using NSCoding and switch to Codable (Combination of Encodable + Decodable) protocol
因此,如果您已经在使用 NSCoding 并切换到 Codable(Encodable + Decodable 的组合)协议,请从您的代码中删除以下两种方法
if let encoded = try? JSONEncoder().encode(Person(name: "Dhaval")) {
UserDefaults.standard.set(encoded, forKey: "kSavedPerson")
}
Let's get started with the simplicity of Codable.
让我们从 Codable 的简单性开始。
Create a custom classor structyou want to store in UserDefaults:
创建要存储在 UserDefaults 中的自定义类或结构:
guard let savedPersonData = UserDefaults.standard.object(forKey: "kSavedPerson") as? Data else { return }
guard let savedPerson = try? JSONDecoder().decode(Person.self, from: savedPersonData) else { return }
print("\n Saved person name : \(savedPerson.name)")
Save object in UserDefaults as below:
将对象保存在 UserDefaults 中,如下所示:
/// A type that adds an interface to use the user's defaults with codable types
///
/// Example:
/// ```
/// @UserDefaultCodable(key: "nameKey", defaultValue: "Root") var name: String
/// ```
/// Adding the attribute @UserDefaultCodable the property works reading and writing from user's defaults
/// with any codable type
///
@propertyWrapper public struct UserDefaultCodable<T: Codable> {
private let key: String
private let defaultValue: T
/// Initialize the key and the default value.
public init(key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
public var wrappedValue: T {
get {
// Read value from UserDefaults
guard let data = UserDefaults.standard.object(forKey: key) as? Data else {
// Return defaultValue when no data in UserDefaults
return defaultValue
}
// Convert data to the desire data type
let value = try? JSONDecoder().decode(T.self, from: data)
return value ?? defaultValue
}
set {
// Convert newValue to data
let data = try? JSONEncoder().encode(newValue)
// Set value to UserDefaults
UserDefaults.standard.set(data, forKey: key)
}
}
}
Load object from UserDefaults as below:
从 UserDefaults 加载对象如下:
##代码##That's it.
就是这样。
回答by 93sauu
In Swift 5, I would use a property wrapper to simply the code:
在 Swift 5 中,我将使用属性包装器来简化代码:
##代码##