首页 > 解决方案 > 在 UserDefaults 中存储/检索结构的可编码字典对我不起作用

问题描述

Swift (v 5/5.1) 新手,在 Codables 上遇到了困难……希望从这里的专家那里得到一些建议。

好的,我有一个来自结构的简单字典,其中键是字符串。我想将字典存储在 UserDefaults 中(然后再检索)。这里有一些非常相似的问题,但这些主要是针对嵌套结构的。

第一次尝试(为简单起见删除了错误处理):

public struct PriceStruct:Codable {
    var myPrice: Double
    var myTime: TimeInterval
    var selected: Bool
    var direction: Int
    var myHigh, myLow: Double

    enum CodingKeys: String, CodingKey {
        case myPrice = "myPrice"
        case myTime = "myTime"
        case selected = "selected"
        case direction = "direction"
        case myHigh = "myHigh"
        case myLow = "myLow"
    }
}

var myPrices: [String: PriceStruct] = [:]

// [fill myPrices with some data...]

func savePrices() {
   // error: Attempt to set a non-property-list object
   UserDefaults.standard.set(myPrices, forKey: "prices")
}

func loadPrices() {
   // obviously this doesn't work either
   let myPrices = UserDefaults.standard.data(forKey: "prices")
}

While I assumed from the documentation, that UserDefaults is capable of storing dictionaries, it doesn't - at least for me.
Next thing I tried was using JSONEncoder like this:

    // this time with prior JSON encoding
    func savePrices() {
       // this works
       let json = try! JSONEncoder().encode(myPrices)
       UserDefaults.standard.set(json as Data, forKey: "prices")
    }

    func loadPrices() {
       // this doesn't work
       let json = UserDefaults.standard.data(forKey: "prices")
       let decoder = JSONDecoder()
       let decoded = try! decoder.decode(PriceStruct.self, from json!)
    }

不幸的是,我在尝试从 UserDefaults 加载数据时遇到错误: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "myPrice", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"myPrice\", intValue: nil) (\"myPrice\").", underlyingError: nil))

我尝试的其他变体是将编码的 JSON 转换为 UTF8 编码的字符串并存储/检索这个:

func savePrices() {
   // this works too
   let json = try! JSONEncoder().encode(myPrices)
   UserDefaults.standard.set(String(data: json, encoding: .utf8), forKey: "prices")
}

func loadPrices() {
   // and this doesn't work either
   let json = UserDefaults.standard.string(forKey: "prices")!.data(using: .utf8)

}

因此,从引发的错误来看,CodingKeys 似乎是问题的根源。我尝试使用 NSKeyedArchiver 和 NSKeyedUnarchiver 进行切换,但没有成功。

我真的想知道是否有一个简单/通用的解决方案可以在 UserDefaults 中保存/加载字典?

感谢您的所有意见和建议。谢谢!

标签: xcodensuserdefaultscodableswift5

解决方案


我在我的项目中尝试了下面的代码,这对我有用。

用户模型

public protocol UserModel: Codable, PrimaryKey {

    var id: String { get }
    var firstName: String? { get }
    var lastName: String? { get }
    var userName: String? { get }
    var emails: [String] { get }
}

public struct User: UserModel {

    public let id: String
    public let firstName: String?
    public let lastName: String?
    public let userName: String?
    public let emails: [String]

    public enum CodingKeys: String, CodingKey {
        case id = "Id"
        case firstName = "FirstName"
        case lastName = "LastName"
        case userName = "UserName"
        case emails = "Emails"
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        do {
            self.id = try container.decode(String.self, forKey: .id)
            self.firstName = try container.decodeIfPresent(String.self, forKey: .firstName)
            self.lastName = try container.decodeIfPresent(String.self, forKey: .lastName)
            self.userName = try container.decodeIfPresent(String.self, forKey: .userName)
            self.emails = try container.decodeIfPresent([String].self, forKey: .emails) ?? []
        }
        catch let error {
            debugPrint(error)
            throw error
        }
    }
}

我已使用以下方式存储在 userDefault

用户数据类

class UserData: NSObject
{

let userDefaultKey = "user_information"

var userData: User?

    func getDictionary() -> [String: Data]
    {
        var dicInfo = [String: Data]()

        do
        {
            let _userData = try JSONEncoder().encode(userData)

            dicInfo["userData_default"]  = _userData

        }catch let error{
            print("error while save data in User Default: \(error.localizedDescription)")
        }

        return dicInfo
    }
    func saveToDefault()
    {
        let userDefault = UserDefaults.standard
        userDefault.set(getDictionary(), forKey: userDefaultKey)
        userDefault.synchronize()
    }
    func loadFromDefault()
    {
        let userDefault = UserDefaults.standard
        if let dicInfo = userDefault.object(forKey: userDefaultKey) as? [String: Data]
        {
            update(dicInfo)
        }
    }
    func update(_ dictionaryInfo: [String: Data])
    {
        do
        {
            if let _userData_data = dictionaryInfo["userData_default"]
            {
                if let _userData = try? JSONDecoder().decode(User.self, from: _userData_data) {
                    userData = _userData
                }
            }

            saveToDefault()

        }catch let error{
            print("error while load From Default data in User Default: \(error.localizedDescription)")
        }
    }
}

希望这会帮助你。


推荐阅读