如何使用可编码值对字典进行编码/解码以存储在UserDefaults中?

时间:2019-06-26 04:49:09

标签: nsuserdefaults codable swift5 swift-dictionary nsarchiving

我试图在iOS UserDefaults中存储映射到Company对象(来自struct Company)的公司名称(字符串)的字典。我创建了Company结构并使其符合Codable。我有一个示例,在我的项目中,有一个朋友帮我完成了一个示例:我们创建了一个Account类,并通过制作Defaults结构将其存储在UserDefaults中(将包括示例代码)。我已经在快速文档中阅读到字典符合Codable,并且为了保持Codable,必须包含Codable对象。这就是为什么我使struct Company符合Codable。

我为Company创建了一个符合Codable的结构。我尝试使用模型代码创建一个新的结构CompanyDefaults来处理从UserDefaults到CompanyDefaults的Company字典的获取和设置。我觉得我对需要发生的事情以及应该如何实现(应该有良好的设计)有一些误解。

我要存储的字典看起来像[String:Company] 公司名称为String,公司名称为Company

在进行一些研究时,我使用了符合Codable的规范,这似乎是完成类似任务的一种较新的方法。

公司结构

struct Company: Codable {
    var name:String?
    var initials:String? = nil
    var logoURL:URL? = nil
    var brandColor:String? = nil // Change to UIColor

    enum CodingKeys:String, CodingKey {
        case name = "name"
        case initials = "initials"
        case logoURL = "logoURL"
        case brandColor = "brandColor"
    }

    init(name:String?, initials:String?, logoURL:URL?, brandColor:String?) {
        self.name = name
        self.initials = initials
        self.logoURL = logoURL
        self.brandColor = brandColor
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        initials = try values.decode(String.self, forKey: .initials)
        logoURL = try values.decode(URL.self, forKey: .logoURL)
        brandColor = try values.decode(String.self, forKey:   .brandColor)

    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        try container.encode(initials, forKey: .initials)
        try container.encode(logoURL, forKey: .logoURL)
        try container.encode(brandColor, forKey: .brandColor)
    }

}

默认结构可控制存储空间

struct CompanyDefaults {

    static private let companiesKey = "companiesKey"

    static var companies: [String:Company] = {

        guard let data = UserDefaults.standard.data(forKey: companiesKey) else { return [:] }
        let companies = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [String : Company] ?? [:]
        return companies!
        }() {
        didSet {
            guard let data = try? NSKeyedArchiver.archivedData(withRootObject: companies, requiringSecureCoding: false) else {
                return
            }

            UserDefaults.standard.set(data, forKey: companiesKey)
        }
    }
}

我应该能够在整个代码中引用存储的字典,例如CompanyDefaults.companies.count

作为参考,一个朋友帮助我对存储在用户默认值中的一系列Account类执行了类似的任务。完美的代码如下。我尝试使用其他方法的原因是我拥有不同的数据结构(字典),并决定使用结构。

class Account: NSObject, NSCoding {

    let service: String
    var username: String
    var password: String

    func encode(with aCoder: NSCoder) {
        aCoder.encode(service)
        aCoder.encode(username)
        aCoder.encode(password)
    }

    required init?(coder aDecoder: NSCoder) {
        guard let service = aDecoder.decodeObject() as? String,
        var username = aDecoder.decodeObject() as? String,
        var password = aDecoder.decodeObject() as? String else {
            return nil
        }

        self.service = service
        self.username = username
        self.password = password

    }

    init(service: String, username: String, password: String) {
        self.service = service
        self.username = username
        self.password = password
    }

}
struct Defaults {

    static private let accountsKey = "accountsKey"

    static var accounts: [Account] = {

        guard let data = UserDefaults.standard.data(forKey: accountsKey) else { return [] }

        let accounts = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Account] ?? []
        return accounts

        }() {
        didSet {
            guard let data = try? NSKeyedArchiver.archivedData(withRootObject: accounts, requiringSecureCoding: false) else {
                return
            }
            UserDefaults.standard.set(data, forKey: accountsKey)
        }
    }

}

1 个答案:

答案 0 :(得分:0)

您正在混合NSCodingCodable。前者需要NSObject的子,后者可以直接用JSONEncoderProperListEncoder编码结构和类,而无需使用任何Keyedarchiver属于NSCoding

您的结构可以简化为

struct Company: Codable {
    var name : String
    var initials : String
    var logoURL : URL?
    var brandColor : String?
}

仅此而已,CodingKeys和其他方法已经合成。我至少将nameinitials声明为非可选。

读取和保存数据非常简单。相应的CompanyDefaults结构是

struct CompanyDefaults {

    static private let companiesKey = "companiesKey"

    static var companies: [String:Company] = {

        guard let data = UserDefaults.standard.data(forKey: companiesKey) else { return [:] }
        return try? JSONDecoder.decode([String:Company].self, from: data) ?? [:]
        }() {
        didSet {
            guard let data = try? JSONEncoder().encode(companies) else { return }
            UserDefaults.standard.set(data, forKey: companiesKey)
        }
    }
}