Reputation: 3057
Purpose
I have a User class with a status property and I want store it into NSUserDefault which need to encode the User class first.
User class code updated version
public class User: NSObject, NSCoding {
override init() {}
var status: Status = .unKnow
required convenience public init(coder aDecoder: NSCoder) {
self.init()
self.status = aDecoder.decodeObject(forKey: "UserStatus") as? Status ?? .unKnow
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(self.status, forKey: "UserStatus")
}
public func updatePersistentData() {
let userDefault = UserDefaults.standard
/// stuck at this line
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: self)
userDefault.set(encodedData, forKey: "User")
userDefault.synchronize()
}
}
And my nested enumeration
enum Status {
/// is Login status
case isLogin(style: LoginStatus)
/// is register
case isRegister(style: RegisterStatus)
/// is fetch user info
case isGetUserInfo(style: GetUserInfoStatus)
/// nonabove
case unKnow
}
enum LoginStatus: Int {
case a
case b
case c
case d
}
enum RegisterStatus: Int {
case a
case b
case c
case d
}
enum GetUserInfoStatus: Int {
case a
case b
case c
case d
}
I found this, and understand I need to convert enum into raw value. It seems need to use a normal enum with string-style or Int-style...etc.
And I run the code, error message
HJC[2371:403228] -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x7f940e6bec30
HJC[2371:403228] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x7f940e6bec30'
Upvotes: 1
Views: 816
Reputation: 5259
I tried to encode and decode the status since it's the only property in the class but you might need to do the same for the other properties if any was found
First i started with giving the State
enum a value that i can encode
enum Status {
enum StatusValue {
case isLogin(LoginStatus)
case isRegister(RegisterStatus)
case isGetUserInfo(RegisterStatus)
case unknow
}
}
extension Status.StatusValue {
var value: Int {
switch self {
case .isLogin(let value):
return value.rawValue
case .isRegister(let value):
return value.rawValue
case .isGetUserInfo(let value):
return value.rawValue
case .unknow:
return -1
}
}
}
enum LoginStatus: Int {
case a = 0
case b
case c
case d
}
enum RegisterStatus: Int {
case a = 4
case b
case c
case d
}
enum GetUserInfoStatus: Int {
case a = 8
case b
case c
case d
}
Second I configured the User
class to implement NSCoding
public class User: NSObject, NSCoding {
override init() {
status = .unknow
}
init(_ status: Status.StatusValue) {
self.status = status
}
var status: Status.StatusValue
public func encode(with aCoder: NSCoder) {
print(self.status.value)
aCoder.encode(self.status.value, forKey: "status")
}
public required convenience init(coder aDecoder: NSCoder) {
let status = aDecoder.decodeObject(forKey: "status") as? Status.StatusValue ?? .unknow
self.init(status)
}
func save() {
let savedData = NSKeyedArchiver.archivedData(withRootObject: self)
let defaults = UserDefaults.standard
defaults.set(savedData, forKey: "user")
defaults.synchronize()
}
}
Finally I tested the result through
let user1: User = User()
user1.status = .isLogin(LoginStatus.b)
user1.save()
let user2: User
let defaults = UserDefaults.standard
if let saveduser = defaults.object(forKey: "user") as? Data {
user2 = NSKeyedUnarchiver.unarchiveObject(with: saveduser) as! User
print(user2)
}
I would also suggest to read a little about it in here: NSCoding, Workaround for Swift Enum with raw type + case arguments?
Upvotes: 2