NSKeyedArchiver - Unrecognized selector sent to instance error
Asked Answered
I

3

6

I'm facing unrecognized selector sent to instance error while using NSKeyedArchiver.archivedData.

I was trying to store parse the JSON output to the custom class object and store it in NSUserDefaults on successful login. I wanted to use these details in other controllers to make some other POST calls. But while saving them in my LoginController I'm getting this error. Can some one help in resolving this.

My Class:

class UserDetails : NSObject,NSCoding {


    var token:String?
    var agentId: Int?
    var userId:Int?


    //to directly get values from JSON output

    init?(user: [String: Any]) {

        guard let token = user["token"] as? String,
            let agentId = user["agent_id"] as? Int,
            let userId = user["user_id"] as? Int
        else{
            return nil
        }


                self.token = token;
                self.agentId = agentId;
                self.userId = userId;
        }

    init(pToken:String,pAgentId:Int,pUserId:Int)
    {
        token = pToken;
        agentId = pAgentId;
        userId = pUserId;
    }

    //TO SAVE IN NSUSERDEFAULTS

    required init?(coder aDecoder: NSCoder){
        self.token = aDecoder.decodeObject(forKey: "token") as? String
        self.agentId = aDecoder.decodeObject(forKey: "agentId") as? Int
        self.userId = aDecoder.decodeObject(forKey: "userId") as? Int

        return self
    }

    public func encode(with aCoder: NSCoder) {
        aCoder.encode(token,forKey:"token")
        aCoder.encode(agentId,forKey:"agentId")
    }

}

And on successful login, I'm storing this details.

LoginViewController:

if let userDict = json["user"] as? [String:Any]
                        {
                            guard let userObject = UserDetails(user:userDict) else {
                                print("Failed to create user from dictionary")
                                return
                            } 
                            DispatchQueue.main.async {

                                self.saveUserItemToDefault(userObject: userObject);

                                self.dismiss(animated: true, completion: nil)
                                self.performSegue(withIdentifier: "loginSuccessIdentifier", sender: userObject)

                            }
                        }




 func saveUserItemToDefault(userObject:UserDetails) -> Void {
            let encodedToken = NSKeyedArchiver.archivedData(withRootObject: userObject.token) //error is thrown on the first archiver call
            let encodedAgentId = NSKeyedArchiver.archivedData(withRootObject: userObject.agentId)
            let encodedUserId = NSKeyedArchiver.archivedData(withRootObject: userObject.userId)

let encodedArray:[NSData] = [encodedToken as NSData,encodedAgentId as NSData,encodedUserId as NSData]

            self.prefs.set(encodedArray, forKey: "UserDetailsItem")
            self.prefs.synchronize()
        }

ERROR MESSAGE:

2016-12-05 18:08:57.917 ABCAPP[2800:183556] -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x600000079f00
2016-12-05 18:08:57.920 ABCAPP[2800:183556] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x600000079f00'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000104b5e34b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x00000001045bf21e objc_exception_throw + 48
    2   CoreFoundation                      0x0000000104bcdf34 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x0000000104ae3c15 ___forwarding___ + 1013
    4   CoreFoundation                      0x0000000104ae3798 _CF_forwarding_prep_0 + 120
    5   Foundation                          0x00000001040f855c _encodeObject + 1263
    6   Foundation                          0x000000010412d29a +[NSKeyedArchiver archivedDataWithRootObject:] + 156
    7   ABCAPP                     0x0000000103fa8d4b _TFC15ABCAPP19LoginViewController21saveUserItemToDefaultfT10userObjectCS_11UserDetails_T_ + 235
    8   ABCAPP                     0x0000000103face3d _TFFFC15ABCAPP19LoginViewController10checkLoginFPs9AnyObject_T_U_FTGSqV10Foundation4Data_GSqCSo11URLResponse_GSqPs5Error___T_U_FT_T_ + 77
    9   ABCAPP                     0x0000000103f9d627 _TTRXFo___XFdCb___ + 39
    10  libdispatch.dylib                   0x0000000108112980 _dispatch_call_block_and_release + 12
    11  libdispatch.dylib                   0x000000010813c0cd _dispatch_client_callout + 8
    12  libdispatch.dylib                   0x000000010811c8d6 _dispatch_main_queue_callback_4CF + 406
    13  CoreFoundation                      0x0000000104b224f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    14  CoreFoundation                      0x0000000104ae7f8d __CFRunLoopRun + 2205
    15  CoreFoundation                      0x0000000104ae7494 CFRunLoopRunSpecific + 420
    16  GraphicsServices                    0x000000010910da6f GSEventRunModal + 161
    17  UIKit                               0x0000000104f81f34 UIApplicationMain + 159
    18  ABCAPP                     0x0000000103faff2f main + 111
    19  libdyld.dylib                       0x000000010818868d start + 1
    20  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Instantaneous answered 5/12, 2016 at 13:31 Comment(2)
What's the whole error message? It could help to find exactly where is the issue rather than read all your code.Insecticide
@Insecticide added error message. Please have a look.Instantaneous
I
2

Along with inputs from JPetric and Valentin Radu I have modified the code by a little and the error is removed.

Below is my solution:

class UserDetails : NSObject,NSCoding {

      //var token:String? 
      var token:String!

      //var agentId: Int? 
      var agentId: Int!

      //var userId:Int? 
      var userId:Int!


    //to directly get values from JSON output

    init?(user: [String: Any]) {

        guard let token = user["token"] as? String,
            let agentId = user["agent_id"] as? Int,
            let userId = user["user_id"] as? Int
        else{
            return nil
        }


                self.token = token;
                self.agentId = agentId;
                self.userId = userId;
        }

    init(pToken:String,pAgentId:Int,pUserId:Int)
    {
        token = pToken;
        agentId = pAgentId;
        userId = pUserId;
    }

    //TO SAVE IN NSUSERDEFAULTS

    required init?(coder aDecoder: NSCoder){
        self.token = aDecoder.decodeObject(forKey: "token") as? String
        self.agentId = aDecoder.decodeObject(forKey: "agentId") as? Int
        self.userId = aDecoder.decodeObject(forKey: "userId") as? Int

        return self
    }

    public func encode(with aCoder: NSCoder) {
        aCoder.encode(token,forKey:"token")
        aCoder.encode(agentId,forKey:"agentId")
    }

}
Instantaneous answered 6/12, 2016 at 3:19 Comment(0)
B
5

The issue might be caused because you are trying to archive an optional and Optional doesn't have the encodeWithCoder method (hence the error message).

Try doing this:

public func encode(with aCoder: NSCoder) {
        if let token = token {
            aCoder.encode(token,forKey:"token")
        }
        if let agentId = agentId {
            aCoder.encode(agentId,forKey:"agentId")
        }
    }
Biota answered 5/12, 2016 at 13:49 Comment(6)
hi JPetric. I tried changing the code to what you mentioned above. Still same error.Instantaneous
Just for my curtesy, can you try to declare token and agentId as String and Int (not optionals) in the declaration of the class (var token: String instead of var token: String?) ?Biota
I tried changing it to what you suggested(not optionals). Now it is throwing an error something like this Initializer for conditional binding must have Optional type, not string near if let token = token { aCoder.encode(token,forKey:"token")}Instantaneous
Just remove if let token = token and if let agentId = agentId from the encode metodBiota
now the code in required init? has been throwing an error. like self.token = aDecoder.decodeObject(forKey: "token") as? String should be replaced by (aDecoder.decodeObject(forKey: "token") as? String)!Instantaneous
Hi JPetric. I was able to solve the error just by changing the declaration from ? to ! var token:String? to var token:String!Instantaneous
T
3

In addition to what @JPetric already said, which might me true, when you get this kind of exceptions, in general, the problem is that you try to archive a data type that's not representable in Objective-C. This can be an optional, a swift enum, a swift closure etc. Add a exception breakpoint to see the actual object that fails to encode and then find a way to use instead an Objective-C object.

Tobitobiah answered 5/12, 2016 at 14:0 Comment(1)
I agree. Adding an exception breakpoint is a must (#17803162)Biota
I
2

Along with inputs from JPetric and Valentin Radu I have modified the code by a little and the error is removed.

Below is my solution:

class UserDetails : NSObject,NSCoding {

      //var token:String? 
      var token:String!

      //var agentId: Int? 
      var agentId: Int!

      //var userId:Int? 
      var userId:Int!


    //to directly get values from JSON output

    init?(user: [String: Any]) {

        guard let token = user["token"] as? String,
            let agentId = user["agent_id"] as? Int,
            let userId = user["user_id"] as? Int
        else{
            return nil
        }


                self.token = token;
                self.agentId = agentId;
                self.userId = userId;
        }

    init(pToken:String,pAgentId:Int,pUserId:Int)
    {
        token = pToken;
        agentId = pAgentId;
        userId = pUserId;
    }

    //TO SAVE IN NSUSERDEFAULTS

    required init?(coder aDecoder: NSCoder){
        self.token = aDecoder.decodeObject(forKey: "token") as? String
        self.agentId = aDecoder.decodeObject(forKey: "agentId") as? Int
        self.userId = aDecoder.decodeObject(forKey: "userId") as? Int

        return self
    }

    public func encode(with aCoder: NSCoder) {
        aCoder.encode(token,forKey:"token")
        aCoder.encode(agentId,forKey:"agentId")
    }

}
Instantaneous answered 6/12, 2016 at 3:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.