Search code examples
arraysswiftnsobjectnscodinghashable

Can't use Hashable while encoding nsarray of custom objects


I have a custom class for mapping like below.

class UserSaved {

    var Id         : String
    var UserName   : String
    var profileURL      : String
    var fullNameUser    : String

    func encode(with aCoder: NSCoder) {
        aCoder.encode(self.Id, forKey: "Id")
        aCoder.encode(self.UserName, forKey: "UserName")
        aCoder.encode(self.profileURL, forKey: "profileURL")
        aCoder.encode(self.fullNameUser, forKey: "fullNameUser")
    }

    required init?(coder aDecoder: NSCoder) {
        self.Id = aDecoder.decodeObject(forKey: "Id") as! String
        self.UserName = aDecoder.decodeObject(forKey: "UserName") as! String
        self.profileURL = aDecoder.decodeObject(forKey: "profileURL") as! String
        self.fullNameUser = aDecoder.decodeObject(forKey: "fullNameUser") as! String

       // self.init(my_id: self.Id, my_username: self.UserName, profile_url: self.profileURL, full_name: self.fullNameUser)
    }

    init(my_id: String, my_username: String, profile_url: String, full_name : String) {
        self.Id = my_id
        self.UserName = my_username
        self.profileURL = profile_url
        self.fullNameUser = full_name
    }
}

Now I am adding the different model object into two different arrays like below, below both arrays may have some common entries

//////array 1
let arrayTemp = dic.value(forKey: "data") as! NSArray
for i in 0..<arrayTemp.count {
    let dic = arrayTemp.object(at: i) as! NSDictionary
    let strId = String(dic.value(forKey: "pk") as! Int)
    let instaName = dic.value(forKey: "username") as! String
    let profileURL = dic.value(forKey: "profile_pic_url") as! String
    let fullName = dic.value(forKey: "full_name") as! String
    let person1 = UserSaved(insta_id: strId, insta_username: instaName,profile_url: profileURL,full_name: fullName)
    self.arr1.append(person1)
}

//////array 2
let arrayTemp = dic.value(forKey: "data") as! NSArray
for i in 0..<arrayTemp.count {
    let dic = arrayTemp.object(at: i) as! NSDictionary
    let strId = String(dic.value(forKey: "pk") as! Int)
    let instaName = dic.value(forKey: "username") as! String
    let profileURL = dic.value(forKey: "profile_pic_url") as! String
    let fullName = dic.value(forKey: "full_name") as! String
    let person2 = UserSaved(insta_id: strId, insta_username: instaName,profile_url: profileURL,full_name: fullName)
    self.arr2.append(person2)
}

Now I am manupalting above arrays using below code, which works fine

let setA = Set(self.arr1)
let setB = Set(self.arr2)
let inBothAAndB = setA.intersection(setB)
let inAButNotB = setA.subtracting(setB)
let inBButNotA = setB.subtracting(setA)

Now when I tried to save the array into userdefaults using NSKeyedArchiver.archivedData. It gives me error like below

2018-10-09 12:27:46.375587+0530 Insta_Analytics[12764:181604] *** NSForwarding: warning: object 0x6100000f0500 of class 
'Insta_Analytics.UserSaved' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[Insta_Analytics.UserSaved replacementObjectForKeyedArchiver:]
2018-10-09 12:27:46.376141+0530 Insta_Analytics[12764:181604] 
Unrecognized selector -[Insta_Analytics.UserSaved replacementObjectForKeyedArchiver:]

Here is my code of saving array into userdefaults

                        let encodedData = NSKeyedArchiver.archivedData(withRootObject: self.arr1)
                    UserDefaults.standard.set(encodedData, forKey: "arr_store")

Now if I change my custom mapping model UserSaved inherit from NSCoding and NSObject. It works, but that time manipulating data is not working

SO at one time, only one thing works. I want to save my arrays using NSCoding while at the same time intersection and subtracting should work


Solution

  • To use NSCoder the class must be a subclass of NSObject

    class UserSaved : NSObject, NSCoding {
    

    But in your case I recommend to use Codable and add the conformance to Equatable

    class UserSaved : Codable, Equatable {
    

    Delete the init(coder and encode(with methods and serialize the data

    let encodedData = PropertyListEncoder().encode(self.arr1)
    

    And don't use NSArray / NSDictionary in Swift. Use native types. And don't use value(forKey. Use key subscripting.

    To decode the data use

    if let data = UserDefaults.standard.data(forKey: "arr_store") {
        do {
            let array = try PropertyListDecoder().decode([UserSaved].self, from: data)
    
        } catch { print(error) }
    }