2

I have an entity in which i have some attributes to which I have pass custom class which store data in an array of that class My core data entity looks like this,

enter image description here

My NSManangedObject Class looks like this,

extension SingleChat {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<SingleChat> {
        return NSFetchRequest<SingleChat>(entityName: "SingleChat")
    }

    @NSManaged public var name: String?
    @NSManaged public var roomSID: String?
    @NSManaged public var isGroup: Bool
    @NSManaged public var lastMessage: String?
    @NSManaged public var lastMsgTime: String?
    @NSManaged public var lastMsgTimeActual: String?
    @NSManaged public var profilePic: String?
    @NSManaged public var lastMsgRead: Bool
    @NSManaged public var unReadMsgsCount: Int16
    @NSManaged public var actualNameFor_1_2_1_chat: String?
    @NSManaged public var isNewGroup: Bool
    @NSManaged public var members : [TCHMember]
    @NSManaged public var messages : [TCHMessage]
    @NSManaged public var twChannelObj: TCHChannel?
    @NSManaged public var groupInfo : [String:JSON]?
}

This is how i save my data in core data ,

 let appDelegate = UIApplication.shared.delegate as? AppDelegate
        let context = appDelegate?.persistentContainer.viewContext
        let entity = NSEntityDescription.entity(forEntityName: "SingleChat", in: context!)
        let user = NSManagedObject(entity: entity!, insertInto: context)

        for items in dateWiseSortedSingleRooms
        {
            user.setValue(items.name, forKey: "name")
            user.setValue(items.actualNameFor_1_2_1_chat, forKey: "actualNameFor_1_2_1_chat")
            user.setValue(items.isGroup, forKey: "isGroup")
            user.setValue(items.lastMsgRead, forKey: "lastMsgRead")
            user.setValue(items.lastMsgTimeActual, forKey: "lastMsgTimeActual")
            user.setValue(items.lastMessage, forKey: "lastMessage")
            user.setValue(items.lastMsgTime, forKey: "lastMsgTime")
            user.setValue(items.profilePic, forKey: "profilePic")
            user.setValue(items.roomSID, forKey: "roomSID")
            user.setValue(items.isNewGroup, forKey: "isNewGroup")
            user.setValue(items.unReadMsgsCount, forKey: "unReadMsgsCount")
            user.setValue(items.unReadMsgsCount, forKey: "unReadMsgsCount")
            user.setValue(items.members, forKey: "members")
            user.setValue(items.messages, forKey: "messages")
            user.setValue(items.twChannelObj, forKey: "twChannelObj")
        }

        do {
            try context?.save()
            print("Saved successfully.")
        } catch  {
            print("Fail to save")
        }

Now when my code execute app crashes at this line,

user.setValue(items.members, forKey: "members")
user.setValue(items.messages, forKey: "messages")

With error this,

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TCHMember encodeWithCoder:]: unrecognized selector sent to instance 0x2807a1780

How I can resolve this? and store my custom class array data in it?

Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
Junaid Khan
  • 125
  • 2
  • 13
  • If you recently added new property to your core data structure you need to reinstall the app once. – Dharmesh Kheni Feb 18 '19 at 12:08
  • No i haven't added new property it is showing me error while saving this line of code, user.setValue(items.members, forKey: "members"). @DharmeshKheni – Junaid Khan Feb 18 '19 at 12:17
  • This may help: https://stackoverflow.com/questions/45211115/how-do-you-save-a-custom-class-as-an-attribute-of-a-coredata-entity-in-swift-3 – Dharmesh Kheni Feb 18 '19 at 12:30
  • My TCHMember AND TCHMessage class is basically twilio classes with base class of NSObject, now how i can set there encoder? @DharmeshKheni – Junaid Khan Feb 18 '19 at 13:01
  • May be you can add encoder to their class? – Dharmesh Kheni Feb 18 '19 at 13:13
  • This is what it shows when i Jump to their definition, /** Representation of a Member on a chat channel. */ interface TCHMember : NSObject . I can't add encoder to their class. @DharmeshKheni – Junaid Khan Feb 18 '19 at 13:16
  • The error is pretty clear: Both classes `TCHMember` and `TCHMessage` must adopt `NSCoding` as I already said in your [previous question](https://stackoverflow.com/questions/54628168/nsmanaged-property-cannot-have-an-initial-value). Why don't you use entities instead? – vadian Feb 20 '19 at 15:04

1 Answers1

2

If you want to store non-standard objects in core data, the corresponding attribute is of type transformable, so that it can be converted to NSData and back.
This can be done if the object adopts the NSCoding protocol, which requires to implement the functions encode(with:) and init(coder:) (described there). These functions define which properties of your custom object have to be stored and restored.
An example how to use these functions can be found here.

EDIT:

There is another way to handle transformable attributes: Instead of adopting the NSCoding protocol, you could implement your custom NSValueTransformer. This class can be implemented independently of your 3rd party classes, see here, and is specified in your core data model, see here. An example can be found here.

EDIT 2:

I did not use the 2nd method by myself, but my impression is:
In oder to store a custom object in core data, it must be converted to NSData, and during a fetch NSData must be converted back to your object.

One way to do so is to use NSCoding, but this requires to implement it in the custom class, maybe as an extension. If this is not possible, as in your case, you can implement a custom NSValueTransformer object.

For encoding, it is given one of your custom objects as input, and creates as appropriate an NSData object from the relevant properties. For decoding, it accepts a NSData object, and initialises a new instance of your custom object, and sets these properties.

You just have to specify the name of the NSValueTransformer in your core data model, and core data will use this custom NSValueTransformer. You can find an example here.

Reinhard Männer
  • 14,022
  • 5
  • 54
  • 116
  • Thanks for this concise answer, i have one question related to my criteria, i'm converting my attribute "members" to Transformable and passes the the array of [TCHMember] to its custom class. Now TCHMember is a class of third party library twilio. How can i encode it first it and than store data in it? As you can see in above question i'm saving data in that attribute. @Reinhard Männer – Junaid Khan Feb 18 '19 at 14:10
  • Oh, I am sorry, I was not aware that these classes are third party (I regarded twilio as a typo of two). Maybe it is possible to write an extension to these classes in which both functions are declared? – Reinhard Männer Feb 18 '19 at 14:14
  • This TCHMember is declared as interface TCHMember : NSObject in TCHMember class of twilio and same TCHMessage. @Reinhard Männer – Junaid Khan Feb 18 '19 at 14:32
  • I'm passing NSCoder with class of NSManagedObject like this, @objc(SingleChat) public class SingleChat: NSManagedObject,NSCoder { I'm getting an error Multiple inheritance from classes 'NSManagedObject' and 'NSCoder'. Why is this so? @Reinhard Männer – Junaid Khan Feb 18 '19 at 15:16
  • @Junaid Khan Did you read my edit? I think this should solve your problem. – Reinhard Männer Feb 20 '19 at 13:35
  • Yeah i have seen your edited answer and read the given links also, but i didn't get the idea what NSValueTransformer do and how should i pass my custom classes to it and how it will store the value? @Reinhard Männer – Junaid Khan Feb 20 '19 at 14:06
  • I tried to be more specific. Please see my edit 2 above. Good luck! If you are successful, maybe you can even post your own answer so that others can learn how to do it! – Reinhard Männer Feb 20 '19 at 14:44