确定可以存储在 UserDefaults 中的 Swift 类型
Determining Swift Types That Can Be Stored in UserDefaults
我正处于发展的初级阶段 an open-source utility for storing state in the Bundle UserDefaults
。
我在向 [String: Any]
的 Dictionary
添加非 Codable
数据类型时遇到问题。
我需要能够在尝试提交之前检查数据,因为 UserDefaults.set(_:)
方法不会抛出任何错误。它只是崩溃。
所以我想确保我提交的 Dictionary
是犹太洁食。
我不能只检查它是否是 Codable
,因为它有时会说它不是,而实际上结构是好的。 (就是一个Dictionary<String, Any>
,我可以把各种东西都塞进去)
我需要验证 Dictionary
是否可以生成 plist。如果这是 ObjC,我可能会使用 NSPropertyListSerialization
方法之一来测试 Dictionary
,但似乎这组方法不适用于 Swift。
根据 the UserDefaults
docs,有一组特定类型和 类 是 "plist-studly."
我认为测试列表中的每种类型是不可接受的。我需要看看我是否能找到一种方法来测试它不会在 Apple 第一次更新 OS.
时被搞砸
有什么好的方法可以测试 Dictionary<String, Any>
是否会让 UserDefaults.set(_:)
呕吐?
UserDefaults
的属性列表类型集非常有限。支持的类型有
NSString
→SwiftString
NSNumber
→ Swift Int
、Double
或 Bool
NSDate
→SwiftDate
NSData
→SwiftData
- 包含 4 种值类型的数组和字典。
Any
不受支持,除非它表示 4 种值或 2 种集合类型之一。
属性 列表兼容的集合类型可以用 PropertyListSerialization
写入 UserDefaults
(即使在 Swift 中)。
有两种协议可以将自定义类型序列化为 Data
Codable
可以序列化结构和 类.
NSCoding
可以序列化 NSObject
的子 类。
structs/classes 中的所有类型必须是可编码和可解码的(意味着符合协议本身)。
PropertyListSerialization
/ PropertyListEncoder/-Decoder
和 NSKeyed(Un)Archiver
的 API 提供强大的错误处理功能以避免崩溃。
UPDATE[1]: 而且,正因为我喜欢分享,here's the actual completed project (MIT License, as is most of my stuff)
更新: This is the solution I came up with。尽管我对 vadian 的出色回答进行了绿色检查,但我还是决定挑剔一点。
感谢 matt 指出我在错误的沙发垫下寻找钥匙,我找到了 NSPropertyListSerialization
的 Swift 变体,我用它来检查树。我怀疑我需要在完成之前将其重构为递归爬虫,但这暂时有效。
这是撰写本文时 _save()
方法的代码。有效:
/* ################################################################## */
/**
This is a private method that saves the current contents of the _values Dictionary to persistent storage, keyed by the value of the "key" property.
- throws: An error, if the values are not all codable.
*/
private func _save() throws {
#if DEBUG
print("Saving Prefs: \(String(describing: _values))")
#endif
// What we do here, is "scrub" the values of anything that was added against what is expected.
var temporaryDict: [String: Any] = [:]
keys.forEach {
temporaryDict[[=10=]] = _values[[=10=]]
}
_values = temporaryDict
if PropertyListSerialization.propertyList(_values, isValidFor: .xml) {
UserDefaults.standard.set(_values, forKey: key)
} else {
#if DEBUG
print("Attempt to set non-codable values!")
#endif
// What we do here, is look through our values list, and record the keys of the elements that are not considered Codable. We return those in the error that we throw.
var valueElementList: [String] = []
_values.forEach {
if PropertyListSerialization.propertyList([=10=].value, isValidFor: .xml) {
#if DEBUG
print("\([=10=].key) is OK")
#endif
} else {
#if DEBUG
print("\([=10=].key) is not Codable")
#endif
valueElementList.append([=10=].key)
}
}
throw PrefsError.valuesNotCodable(invalidElements: valueElementList)
}
}
我正处于发展的初级阶段 an open-source utility for storing state in the Bundle UserDefaults
。
我在向 [String: Any]
的 Dictionary
添加非 Codable
数据类型时遇到问题。
我需要能够在尝试提交之前检查数据,因为 UserDefaults.set(_:)
方法不会抛出任何错误。它只是崩溃。
所以我想确保我提交的 Dictionary
是犹太洁食。
我不能只检查它是否是 Codable
,因为它有时会说它不是,而实际上结构是好的。 (就是一个Dictionary<String, Any>
,我可以把各种东西都塞进去)
我需要验证 Dictionary
是否可以生成 plist。如果这是 ObjC,我可能会使用 NSPropertyListSerialization
方法之一来测试 Dictionary
,但似乎这组方法不适用于 Swift。
根据 the UserDefaults
docs,有一组特定类型和 类 是 "plist-studly."
我认为测试列表中的每种类型是不可接受的。我需要看看我是否能找到一种方法来测试它不会在 Apple 第一次更新 OS.
时被搞砸有什么好的方法可以测试 Dictionary<String, Any>
是否会让 UserDefaults.set(_:)
呕吐?
UserDefaults
的属性列表类型集非常有限。支持的类型有
NSString
→SwiftString
NSNumber
→ SwiftInt
、Double
或Bool
NSDate
→SwiftDate
NSData
→SwiftData
- 包含 4 种值类型的数组和字典。
Any
不受支持,除非它表示 4 种值或 2 种集合类型之一。
属性 列表兼容的集合类型可以用 PropertyListSerialization
写入 UserDefaults
(即使在 Swift 中)。
有两种协议可以将自定义类型序列化为 Data
Codable
可以序列化结构和 类.NSCoding
可以序列化NSObject
的子 类。
structs/classes 中的所有类型必须是可编码和可解码的(意味着符合协议本身)。
PropertyListSerialization
/ PropertyListEncoder/-Decoder
和 NSKeyed(Un)Archiver
的 API 提供强大的错误处理功能以避免崩溃。
UPDATE[1]: 而且,正因为我喜欢分享,here's the actual completed project (MIT License, as is most of my stuff)
更新: This is the solution I came up with。尽管我对 vadian 的出色回答进行了绿色检查,但我还是决定挑剔一点。
感谢 matt 指出我在错误的沙发垫下寻找钥匙,我找到了 NSPropertyListSerialization
的 Swift 变体,我用它来检查树。我怀疑我需要在完成之前将其重构为递归爬虫,但这暂时有效。
这是撰写本文时 _save()
方法的代码。有效:
/* ################################################################## */
/**
This is a private method that saves the current contents of the _values Dictionary to persistent storage, keyed by the value of the "key" property.
- throws: An error, if the values are not all codable.
*/
private func _save() throws {
#if DEBUG
print("Saving Prefs: \(String(describing: _values))")
#endif
// What we do here, is "scrub" the values of anything that was added against what is expected.
var temporaryDict: [String: Any] = [:]
keys.forEach {
temporaryDict[[=10=]] = _values[[=10=]]
}
_values = temporaryDict
if PropertyListSerialization.propertyList(_values, isValidFor: .xml) {
UserDefaults.standard.set(_values, forKey: key)
} else {
#if DEBUG
print("Attempt to set non-codable values!")
#endif
// What we do here, is look through our values list, and record the keys of the elements that are not considered Codable. We return those in the error that we throw.
var valueElementList: [String] = []
_values.forEach {
if PropertyListSerialization.propertyList([=10=].value, isValidFor: .xml) {
#if DEBUG
print("\([=10=].key) is OK")
#endif
} else {
#if DEBUG
print("\([=10=].key) is not Codable")
#endif
valueElementList.append([=10=].key)
}
}
throw PrefsError.valuesNotCodable(invalidElements: valueElementList)
}
}