Stephen Chen
Stephen Chen

Reputation: 3057

Encode nested enumeration swift 3

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

Answers (1)

zombie
zombie

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

Related Questions