在 UserDefaults 中存储 class 对象时,枚举编码值为 nil。 Codable Protocol 已经被继承
enum encoded value is nil while storing the class object in UserDefaults. Codable Protocol is already inherited
我是 iOS 的新手,正在尝试将用户对象存储在 UserDefaults 中。这样当应用程序再次启动时,我可以检查用户类型并根据它导航到相关屏幕。
为此,我创建了一个用户 class,如下所示(可编码),它有一个用户类型枚举 属性!
enum UserType: Int, Codable {
case userType1 = 0
case userType2 = 1
case notDetermined = 2
init(from decoder: Decoder) throws {
let label = try decoder.singleValueContainer().decode(Int.self)
self = UserType(rawValue: label) ?? .notDetermined
}
}
class User: Codable {
public var userFullName: String? = ""
public var userType: UserType? //= .notDetermined
enum CodingKeys: String, CodingKey {
case userFullName
}
}
在我的视图控制器 class 中,我正在为用户对象创建一个新实例并尝试存储在用户默认值中,如下所示:
let newUser = User()
newUser.userFullName = "Test"
newUser.userType = userTypeBtn.isSelected ? .userType1 : .userType2
当我打印 newUser 的 userType 时,无论选择哪个,我都能看到正确的值。但在那之后,当我尝试将其存储在 userDefaults 中时,它 returns nil for userType 属性.
do {
let encoded = try JSONEncoder().encode(newValue)
UserDefaults.standard.set(encoded, forKey: UserDefaultKey.currentUser)
UserDefaults.standard.sync()
} catch {
print("Unable to Encode User Object: (\(error))")
}
当我尝试打印这个 编码的 变量,并在控制台中对其进行解码时
JSONDecoder().decode(User.self, from: encoded).userType
它打印 nil。
请帮助我如何在 UserDefaults 中存储可选枚举 属性 并在需要时使用 Codable
检索它
您应该在 CodingKeys
枚举中包含 userType
:
enum CodingKeys: String, CodingKey {
case userFullName
case userType
}
或者完全删除 CodingKeys
枚举,因为默认情况下,所有属性都作为编码键包含在内。 CodingKeys
枚举中的键决定了合成的 Codable
实现将编码和解码的内容。如果不包含 userType
,userType
将不会被编码,因此它不会存储到 UserDefaults
.
I am not getting it from Server and userType is an external property outside the JSON response
这很好,因为 userType
是可选的。如果 JSON 没有密钥,它将被分配 nil
。这可能是一个问题,如果你也在编码 User
并将其发送到服务器, 和 服务器无法处理请求中的额外键,在这种情况下你需要两个结构 - 一个用于存储来自 UserDefaults
的 to/loading,一个用于 parsing/encoding 服务器 response/request.
记得在尝试时将新的 User
编码为 UserDefaults
,因为旧的 userType
仍然没有编码。
观察
- 为
enum UserType: Int, Codable
的 Decodable
部分自定义实现可能不是最好的主意。 Swift 编译器支持 encoding/decoding enum X: Int
开箱即用,无需您为其编写自定义实现。 (事实上 ,从 Swift 5.5 开始,Swift 编译器现在可以为具有关联值的 case 的枚举执行此操作。)
- 你应该尽量避免出现像
.notDetermined
这样的情况。要么用户具有明确定义的类型,要么 user.type 是 nil
。您可以轻松地为用户本身定义便利的 getter 以了解其类型。
- Swift 允许类型嵌套,因此
User.Kind
而不是 UserType
在 Swift. 中更自然
以下实施解决了所有这些问题。
import Foundation
class User: Codable {
enum Kind: Int, Codable {
case free = 1
case pro = 2
}
public var fullName: String?
public var kind: Kind?
}
let newUser = User()
newUser.fullName = "Test"
newUser.kind = .free
do {
let encoded = try JSONEncoder().encode(newUser)
UserDefaults.standard.set(encoded, forKey: "appUser")
if let fetched = UserDefaults.standard.value(forKey: "appUser") as? Data {
let decoded = try JSONDecoder().decode(User.self, from: fetched)
print(decoded)
}
}
以上代码包括定义、构造、encodeAndStore、fetchAndDecode,无需任何自定义实现即可完成您需要的一切。
奖金
上面的代码没有为 User
打印一个很好的描述。为此,您可以像这样添加 CustomStringConvertible
一致性。
extension User: CustomStringConvertible {
var description: String {
"""
fullName: \(fullName ?? "")
kind: \(kind?.description ?? "")
"""
}
}
extension User.Kind: CustomStringConvertible {
var description: String {
switch self {
case .free: return "free"
case .pro: return "pro"
}
}
}
如果你在实施这个之后尝试 print(decoded)
,你会清楚地看到你想看到的 User
实例。
User.kind
可以是 nil
,我不想每次都用 if let
来处理它,每次我需要从应用程序的不同屏幕检查它。
不用担心,可以简化成这样
extension User {
var isFreeUser: Bool { kind == .free }
var isProUser: Bool { kind == .pro }
}
我是 iOS 的新手,正在尝试将用户对象存储在 UserDefaults 中。这样当应用程序再次启动时,我可以检查用户类型并根据它导航到相关屏幕。
为此,我创建了一个用户 class,如下所示(可编码),它有一个用户类型枚举 属性!
enum UserType: Int, Codable {
case userType1 = 0
case userType2 = 1
case notDetermined = 2
init(from decoder: Decoder) throws {
let label = try decoder.singleValueContainer().decode(Int.self)
self = UserType(rawValue: label) ?? .notDetermined
}
}
class User: Codable {
public var userFullName: String? = ""
public var userType: UserType? //= .notDetermined
enum CodingKeys: String, CodingKey {
case userFullName
}
}
在我的视图控制器 class 中,我正在为用户对象创建一个新实例并尝试存储在用户默认值中,如下所示:
let newUser = User()
newUser.userFullName = "Test"
newUser.userType = userTypeBtn.isSelected ? .userType1 : .userType2
当我打印 newUser 的 userType 时,无论选择哪个,我都能看到正确的值。但在那之后,当我尝试将其存储在 userDefaults 中时,它 returns nil for userType 属性.
do {
let encoded = try JSONEncoder().encode(newValue)
UserDefaults.standard.set(encoded, forKey: UserDefaultKey.currentUser)
UserDefaults.standard.sync()
} catch {
print("Unable to Encode User Object: (\(error))")
}
当我尝试打印这个 编码的 变量,并在控制台中对其进行解码时
JSONDecoder().decode(User.self, from: encoded).userType
它打印 nil。
请帮助我如何在 UserDefaults 中存储可选枚举 属性 并在需要时使用 Codable
检索它您应该在 CodingKeys
枚举中包含 userType
:
enum CodingKeys: String, CodingKey {
case userFullName
case userType
}
或者完全删除 CodingKeys
枚举,因为默认情况下,所有属性都作为编码键包含在内。 CodingKeys
枚举中的键决定了合成的 Codable
实现将编码和解码的内容。如果不包含 userType
,userType
将不会被编码,因此它不会存储到 UserDefaults
.
I am not getting it from Server and userType is an external property outside the JSON response
这很好,因为 userType
是可选的。如果 JSON 没有密钥,它将被分配 nil
。这可能是一个问题,如果你也在编码 User
并将其发送到服务器, 和 服务器无法处理请求中的额外键,在这种情况下你需要两个结构 - 一个用于存储来自 UserDefaults
的 to/loading,一个用于 parsing/encoding 服务器 response/request.
记得在尝试时将新的 User
编码为 UserDefaults
,因为旧的 userType
仍然没有编码。
观察
- 为
enum UserType: Int, Codable
的Decodable
部分自定义实现可能不是最好的主意。 Swift 编译器支持 encoding/decodingenum X: Int
开箱即用,无需您为其编写自定义实现。 (事实上 ,从 Swift 5.5 开始,Swift 编译器现在可以为具有关联值的 case 的枚举执行此操作。) - 你应该尽量避免出现像
.notDetermined
这样的情况。要么用户具有明确定义的类型,要么 user.type 是nil
。您可以轻松地为用户本身定义便利的 getter 以了解其类型。 - Swift 允许类型嵌套,因此
User.Kind
而不是UserType
在 Swift. 中更自然
以下实施解决了所有这些问题。
import Foundation
class User: Codable {
enum Kind: Int, Codable {
case free = 1
case pro = 2
}
public var fullName: String?
public var kind: Kind?
}
let newUser = User()
newUser.fullName = "Test"
newUser.kind = .free
do {
let encoded = try JSONEncoder().encode(newUser)
UserDefaults.standard.set(encoded, forKey: "appUser")
if let fetched = UserDefaults.standard.value(forKey: "appUser") as? Data {
let decoded = try JSONDecoder().decode(User.self, from: fetched)
print(decoded)
}
}
以上代码包括定义、构造、encodeAndStore、fetchAndDecode,无需任何自定义实现即可完成您需要的一切。
奖金
上面的代码没有为 User
打印一个很好的描述。为此,您可以像这样添加 CustomStringConvertible
一致性。
extension User: CustomStringConvertible {
var description: String {
"""
fullName: \(fullName ?? "")
kind: \(kind?.description ?? "")
"""
}
}
extension User.Kind: CustomStringConvertible {
var description: String {
switch self {
case .free: return "free"
case .pro: return "pro"
}
}
}
如果你在实施这个之后尝试 print(decoded)
,你会清楚地看到你想看到的 User
实例。
User.kind
可以是 nil
,我不想每次都用 if let
来处理它,每次我需要从应用程序的不同屏幕检查它。
不用担心,可以简化成这样
extension User {
var isFreeUser: Bool { kind == .free }
var isProUser: Bool { kind == .pro }
}