4

I have read lot of topics about this issue but did not really find an appropriate answer. I want to create an instance of NSManagedObject without having context.

Here is the reason: app gets soap answer from the server. This answer must be saved into Core Data. The answer looks like a tree.

My idea is to override init for each entity so it takes data. After that I'll be able to create root entity and creation of root entity will call creation of another entity and so on.

Part of app that is responsible for a making requests is implemented through generics. There is protocol that describes init that each response class must have, e. g.:

public protocol Parsable {
    init(data: Data)
}  

So as you can see there is no room for context here. Instead I want to create all these entities and save it into the context in one go.

The alternative solution here is to make duplicated classes, fill it with response, and then copy it into my Core Data entities. But this is unnecessary duplication that I would like to avoid.

Any ideas will be appreciated.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Yura
  • 43
  • 5
  • Basically you cannot create `NSManagedObject` instances in Core Data without a context. But what is the benefit? You have to insert the instances in the context anyway and the `save` command is supposed to be called **once** after all instances are created and inserted. – vadian May 06 '17 at 10:17
  • Possible duplicate of http://stackoverflow.com/questions/30382590/how-to-use-core-data-model-subclasses-outside-a-core-data-context – Martin R May 06 '17 at 10:20
  • @vadian, thanks for answer. It's true that save will be called once. As I mentioned above, I just do not have access to moc in place where I process my response. – Yura May 06 '17 at 10:39
  • Then I'd use a custom struct as temporary storage. – vadian May 06 '17 at 10:51
  • @vadian, thanks I ended up with this solution for now. – Yura May 06 '17 at 11:43

2 Answers2

1

Actually "The alternative solution here is to make duplicated classes" is the only good solution for very many reasons.

It seems in your case you already have 2 such objects representing the same entity. what you receive from server I assume is a JSON parsed into dictionary. This is an object that represents the same entity. So to transfer this into core data you already have 1 mapper from dictionary to core data managed object.

Assuming that managed objects are good for using directly in higher level of your application is wrong. We use wrappers for that. So you need one class that handles all the data transfer next to having all the interface to use the data in any module and on any thread. That is why you need to transfer all the data to a new class that may wrap the managed object.

So consider you have a class called MyObject and a core data class MyObjectEntity and probably a dictionary for the API. Then the interface of MyObject would be:

init(entity: MyObjectEntity) // Wraps the entity and copies all fields to this class
init(descriptor: [String: Any]) // Copies all fields to this class
var descriptor: [String: Any] // Returns a dictionary ready to be parsed to JSON
func writeToManagedObject() // Will copy all the data to managed object. If the object exists it will modify it, if not it will create a new one. This will not save the database.

With a bit of subclassing and some extensions you may create a very nice system with this even if you have many models that need mapping. And since this class is then completely insensitive to accessing from any thread and/or saving the context it is ready for any other high level operations. As far as I care you may even use it as MVVM.

Matic Oblak
  • 16,318
  • 3
  • 24
  • 43
  • 1
    thanks for the answer. Can you please explain me what is wrong with using managed objects directly in higher level of the application? Thanks. – Yura May 08 '17 at 17:14
  • 1
    Multithreading is an issue.;If you need temporary/discardable objects you need multiple contexts which usually lead to conflicts; They are unpredictable when fault; A single object can be modified at the same time by multiple modules without you knowing it; You have duplicate properties when you need to typecast such as NSDate to Date or UIImage to NSData when using transformable; ... But still, if your applications is simple and if it works for you then do not bother with these things, just use them as you do. – Matic Oblak May 08 '17 at 17:21
0

Make your data class that emits the NSManagedObject subclass when it's time, and is handed off to a part of the code that has a moc. For the rest of the program, just deal with your non-MOC class. Example below.

Example:

class MyMOCDataClass : NSManagedObject {
public var myName:String
public var myAge:Int16
}

class MyDataClass {
public var myName:String
public var myAge:Int16

init(...){ 
//whatever else you may or may not need to do here. 
}

func emitWithMoc( moc: NSManagedObjectContext ) -> MyMOCDataClass {
var tmpMocClass = MyMOCDataClass( context: moc )
tmpMocClass.myName = self.myName
tmpMocClass.myAge = self.myAge
return tmpMocClass;
}
ChrisH
  • 975
  • 12
  • 21