跟进我以前的question:
我设法使我的枚举符合Codable协议,实现了init()和encode(),它似乎工作。
enum UserState {
case LoggedIn(LoggedInState)
case LoggedOut(LoggedOutState)
}
enum LoggedInState: String {
case playing
case paused
case stopped
}
enum LoggedOutState: String {
case Unregistered
case Registered
}
extension UserState: Codable {
enum CodingKeys: String, CodingKey {
case loggedIn
case loggedOut
}
enum CodingError: Error {
case decoding(String)
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
if let loggedIn = try? values.decode(String.self, forKey: .loggedIn) {
self = .LoggedIn(LoggedInState(rawValue: loggedIn)!)
}
else if let loggedOut = try? values.decode(String.self, forKey: .loggedOut) {
self = .LoggedOut(LoggedOutState(rawValue: loggedOut)!)
}
else {
throw CodingError.decoding("Decoding failed")
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .LoggedIn(value):
try container.encode(value.rawValue, forKey: .loggedIn)
case let .LoggedOut(value):
try container.encode(value.rawValue, forKey: .loggedOut)
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let state = UserState.LoggedIn(.playing)
UserDefaults.standard.set(state, forKey: "state")
}
}
我的问题是我不知道如何将其保存到UserDefaults。如果我像现在一样保存它,我在运行应用程序时会出现以下错误:
[User Defaults] Attempt to set a non-property-list object Codable.UserState.LoggedIn(Codable.LoggedInState.playing) as an NSUserDefaults/CFPreferences value for key state
2018-01-20 19:06:26.909349+0200 Codable[6291:789687]
答案 0 :(得分:3)
来自UserDefaults
参考:
默认对象必须是属性列表 - 即(或 对于集合, NSData ,NSString的实例组合, NSNumber,NSDate,NSArray或NSDictionary。如果你想存储任何 在其他类型的对象中,通常应将其存档以创建 NSData的实例。
因此,您应手动编码state
并将其存储为数据:
if let encoded = try? JSONEncoder().encode(state) {
UserDefaults.standard.set(encoded, forKey: "state")
}
然后,读回来:
guard let data = UserDefaults.standard.data(forKey: "state") else { fatalError() }
if let saved = try? JSONDecoder().decode(UserState.self, from: data) {
...
}
答案 1 :(得分:0)
枚举应该具有rawValue才能保存为Codable对象。您的枚举案例具有关联值,因此它们不能具有rawValue。
我的解释
让我们假设您可以将对象.LoggedIn(.playing)
保存到UserDefaults。您想保存UserState
.LoggedIn
案例。有两个主要问题:
应该保存哪些内容?你的枚举案例没有任何价值,没有什么可以保存的。您可能认为它具有关联值,可以保存到存储中。所以第二个问题。
(假设您将其保存到存储中)从存储中获取保存值后,您将如何确定它是什么情况?您可能认为它可以通过关联值的类型来确定,但是如果您有大量具有相同关联值类型的情况会怎么样?所以编译器会这样做,它不知道在这种情况下该怎么做。
您的问题
您正在尝试保存枚举的案例本身,但默认情况下保存到UserDefaults不会转换您的对象。您可以尝试将.LoggedIn
个案例编码为Data
,然后将结果数据保存到UserDefaults。当您需要获得保存的值时,您将从存储中获取数据并从数据中解码枚举的情况。
答案 2 :(得分:0)
快速4 + 。 枚举,具有正常 情况,以及具有关联 值的案件 。
enum Enumeration: Codable {
case one
case two(Int)
private enum CodingKeys: String, CodingKey {
case one
case two
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let value = try? container.decode(String.self, forKey: .one), value == "one" {
self = .one
return
}
if let value = try? container.decode(Int.self, forKey: .two) {
self = .two(value)
return
}
throw _DecodingError.couldNotDecode
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .one:
try container.encode("one", forKey: .one)
case .two(let number):
try container.encode(number, forKey: .two)
}
}
}
let one1 = Enumeration.two(442)
if let encoded = try? encoder.encode(one1) {
UserDefaults.standard.set(encoded, forKey: "savedEnumValue")
}
if let savedData = UserDefaults.standard.object(forKey: "savedEnumValue") as? Data {
if let loadedValue = try? JSONDecoder().decode(Enumeration.self, from: savedData) {
print(loadedValue) // prints: "two(442)"
}
}