1

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'
Community
  • 1
  • 1
Stephen Chen
  • 3,027
  • 2
  • 27
  • 39

1 Answers1

2

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?

Community
  • 1
  • 1
zombie
  • 5,069
  • 3
  • 25
  • 54
  • Thanks for the response, but I still got error `Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x7f940e6bec30' ` in this method **let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: self)** which is write inside the User class – Stephen Chen Mar 13 '17 at 15:05
  • 1
    @StephenChen I noticed from your error you are still using `func encodeWithCoder(aCoder: NSCoder)` instead of `public func encode(with aCoder: NSCoder)`. Is that the case or is this a new error you were not getting before? – Prientus Mar 13 '17 at 15:28
  • @Prientus sorry, I had wrong typing. Thank you for the notice, In my code I did use `public func encode(with aCoder: NSCoder)` – Stephen Chen Mar 13 '17 at 15:39
  • @StephenChen did that solve your problem if not I will take a look at it again when I reach home – zombie Mar 13 '17 at 15:45
  • 1
    @StephenChen finally I managed to get it to work and updated my answer – zombie Mar 13 '17 at 17:57
  • It works like a miracle, thank you @zombie,never thought about using extension。 – Stephen Chen Mar 14 '17 at 02:36