顶级 Bool 编码为数字 属性 列表片段。属性列表编码器

Top-level Bool encoded as number property list fragment. PropertyListEncoder

我有这个通用函数可以保存在 NSUserDefaults 中,通常可以正常工作,但现在我想保存一个布尔值,但出现错误。我找不到任何东西,我不明白为什么它不起作用。

extension UserDefaults {
    func saveUserDefaults<T: Codable>(withKey key: String, myType: T) throws{
        do {
            let data = try PropertyListEncoder().encode(myType)
            UserDefaults.standard.set(data, forKey: key)
            print("Saved for Key:", key)
        } catch let error {

            print("Save Failed")
            throw error
        }
    }

我是这样称呼它的:

try! UserDefaults().saveUserDefaults(withKey: "String", myType: false)

这是我得到的错误。我知道还有另一种保存布尔值的方法,但我想知道为什么它不能像这样工作?

Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.EncodingError.invalidValue(false, Swift.EncodingError.Context(codingPath: [], debugDescription: "Top-level Bool encoded as number property list fragment.", underlyingError: nil))

谢谢!

A PropertyListEncoder 编码为“属性 列表”,这始终是一个 数组或字典,比较 PropertyListSerialization.

因此

let data = try PropertyListEncoder().encode(myType)

如果 myType 是一个 Bool(或任何不是数组的东西)则失败 或字典)。

属性列表中的可能对象也受到限制,它们只能是实例 NSDataNSStringNSArrayNSDictionaryNSDateNSNumber – 或 Swift 类型 桥接到其中一种基础类型。

正如@Martin 所说,PropertyListEncoder 仅支持顶层的 属性 列表,而不支持像 NSNumber 这样的 属性 列表的单个片段。 一个非常简单(虽然不是很优雅)的解决方法是将任何对象包装到数组中:

let data = try PropertyListEncoder().encode([myType])
UserDefaults.standard.set(data, forKey: key)

并将其解码为:

let arr = try  PropertyListDecoder().decode([T].self, from: data)
return arr.first

https://www.marisibrothers.com/2018/07/workaround-for-serializing-codable-fragments.html

您无需对布尔值进行编码即可保存到 UserDefaults 中。您可以通过调用

直接将布尔值保存到 UserDefaults 中
    let myValue: Bool = false
    UserDefaults.standard.set(myValue, forKey: "key")

查看 Apple 文档:UserDefaults.set(_:forKey:)

其实Float、Double、Integer、Bool和URL类型都不需要编码,直接保存到UserDefaults即可。

我看到您有一个函数接受 Codable 类型,对其进行编码,然后将其保存到 UserDefaults。正如 Martin R 指出的那样,您必须修改该函数并检查传递的对象是否可以直接保存到 UserDefaults 而无需编码。它不一定很漂亮,但像这样的东西可以工作:

switch objectToSave {
  case let aFloatType as Float:
    UserDefaults.standard.set(aFloatType, forKey: "key")
  case let aDoubleType as Double:
    UserDefaults.standard.set(aDoubleType, forKey: "key")
  case let anIntegerType as Int:
    UserDefaults.standard.set(anIntegerType, forKey: "key")
  case let aBoolType as Bool:
    UserDefaults.standard.set(aBoolType, forKey: "key")
  case let aURLType as URL:
    UserDefaults.standard.set(aURLType, forKey: "key")
  default:
    //encode the object as a PropertyList and then save it to UserDefaults
    do {
        let data = try PropertyListEncoder().encode(objectToSave)
        UserDefaults.standard.set(data, forKey: key)
    } catch let error {
        //the object you passed cannot be encoded as a PropertyList
        //possibly because the object cannot be represented as
        //NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary - or equivalent Swift types
        //also contents of NSArray and NSDictionary have to be one of the types above
        print("Save Failed: \(error)")
        //perform additional error handling
    }
}