如何 encode/decode 带有 Codable 值的字典用于存储在 UserDefaults 中?
How to encode/decode a dictionary with Codable values for storage in UserDefaults?
我正在尝试在 iOS UserDefaults 中存储映射到公司对象(来自结构公司)的公司名称(字符串)字典。我创建了 Company 结构并使其符合 Codable。我有一个朋友在我的项目中帮助我的例子,我们创建了一个 class Account 并通过制作 Defaults 结构(将包括示例代码)将其存储在 UserDefaults 中。我在 swift 文档中读到字典符合 Codable 并且为了保持 Codable,必须包含 Codable 对象。这就是我让 struct Company 符合 Codable 的原因。
我已经为 Company 创建了一个符合 Codable 的结构。我尝试使用模型代码创建一个新的结构 CompanyDefaults 来处理公司字典 from/to UserDefaults 的获取和设置。我觉得我对需要发生什么以及应该如何实施(考虑到良好的设计)有一些初学者的误解。
我希望存储的字典看起来像 [String:Company]
其中公司名称将是 String 和 Company
的 Company 对象
我在做一些研究时使用了 conform to 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
作为参考,一位朋友帮助我对存储在用户默认值中的帐户 classes 数组执行了类似的任务。下面是完美适用的代码。我尝试不同方式的原因是我有不同的数据结构(字典)并决定使用结构。
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)
}
}
}
您混淆了 NSCoding
和 Codable
。前者需要NSObject
的子class,后者可以直接用JSONEncoder
或[=编码结构体和classes 16=] 没有任何 Keyedarchiver
也属于 NSCoding
.
你的结构可以减少到
struct Company: Codable {
var name : String
var initials : String
var logoURL : URL?
var brandColor : String?
}
就这样,CodingKeys和其他的方法综合起来。我至少会声明 name
和 initials
为 non-optional.
读取和保存数据非常简单。对应的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)
}
}
}
我正在尝试在 iOS UserDefaults 中存储映射到公司对象(来自结构公司)的公司名称(字符串)字典。我创建了 Company 结构并使其符合 Codable。我有一个朋友在我的项目中帮助我的例子,我们创建了一个 class Account 并通过制作 Defaults 结构(将包括示例代码)将其存储在 UserDefaults 中。我在 swift 文档中读到字典符合 Codable 并且为了保持 Codable,必须包含 Codable 对象。这就是我让 struct Company 符合 Codable 的原因。
我已经为 Company 创建了一个符合 Codable 的结构。我尝试使用模型代码创建一个新的结构 CompanyDefaults 来处理公司字典 from/to UserDefaults 的获取和设置。我觉得我对需要发生什么以及应该如何实施(考虑到良好的设计)有一些初学者的误解。
我希望存储的字典看起来像 [String:Company]
其中公司名称将是 String 和 Company
我在做一些研究时使用了 conform to 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
作为参考,一位朋友帮助我对存储在用户默认值中的帐户 classes 数组执行了类似的任务。下面是完美适用的代码。我尝试不同方式的原因是我有不同的数据结构(字典)并决定使用结构。
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)
}
}
}
您混淆了 NSCoding
和 Codable
。前者需要NSObject
的子class,后者可以直接用JSONEncoder
或[=编码结构体和classes 16=] 没有任何 Keyedarchiver
也属于 NSCoding
.
你的结构可以减少到
struct Company: Codable {
var name : String
var initials : String
var logoURL : URL?
var brandColor : String?
}
就这样,CodingKeys和其他的方法综合起来。我至少会声明 name
和 initials
为 non-optional.
读取和保存数据非常简单。对应的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)
}
}
}