I've taught myself Obj-C, and have been self-teaching Cocoa, but adding Core Data to my program has given me nothing but a huge headache, thanks to me needing extensive custom logic. Here are a couple of the questions that are driving me insane.
What if I want a Managed Object to own another Managed Object? It seems if I give it a to-many relationship, the owned object will simply be shared by various masters, but I want each Owner to have its own.
If I subclass an NSManagedObject, can I make simple calls to the Array Controller to remove or copy instances of ManagedObject, and assume those will be translated into the Core Data model?
If I want to programmatically edit the properties of a ManagedObject, can I get away with mere KVC calls to the Array Controller? What's all this talk about NSPredicate an NSFetchRequest to the NSManagedObjectContext from the NSManagedDataStoreDrivingMeCrazy? Can I make an NSFetchRequest that filters the relationships of an object currently selected in a table view?
Once I use a fetch request to get a group of objects, how do I go about querying their relations? Does that require a whole other FetchRequest, Predicate, and so forth? Isn't Core Data supposed to be easier? Am I missing something?
An entity is similar to a class--it's a blueprint for a managed object that will be instantiated later. Each managed object will have its own attributes and relationships to configure.
You can definitely insert and delete managed objects. You might have to do some code to support copying, but I am not sure.
Yes, the properties (attributes and relationships) of managed objects support KVC (and KVO and bindings).
You can access the object or set of objects simply by using the relationship name that you define in the model (no additional fetch or logic is required).
Related
I just have started working with Realm for Swift. After reading the documentation, still I have a couple of question marks in mind.
My biggest one is, if there is a best practice for separation Model classes from Realm classes.
For example, in Java MVC projects we used DAO classes (Data Access Object classes) which were responsible for the communication with the database layer.
Our corresponding model classes only have either the dao object injected or we used service classes for this (like CRUD operations).
If I habe a Realm “Model” class, this seems now to be everything in one. But after having the object persisted to the database, changing attributes of the object in the UI-Layer results in
'Attempting to modify object outside of a write transaction - call
beginWriteTransaction on an RLMRealm instance first.'
Which brings me back to my initial though: Shouldn’t this be separated in Realm objects and model objects. Or is it OK to have "realm.write" processes in View-Classes then?
I did some research on this but the results are very vage on this.
How do you deal with this in your projects. Do you have some kind of best practice or guidance?
Many thanks in advance
John
Officially, (on the iOS side at least), there's no established best practice for abstracting the model class logic away from the actual Realm Object subclasses. That being said, I've definitely heard of apps in the past who do do this sort of logic in order to support multiple types of data frameworks.
If I were to do this, I would make a separate object class for each model implementing my own API on how to get/set data properties, and make the Realm object an internal member of this object. This object would then serve as the generic interface between my app's logic and how to save that data to Realm. That way, if I did want to swap out the data framework down the line, I could simply replace my own data object with a new one, but keep the API consistent.
In regards to that error message you posted, in order to ensure data integrity, you cannot modify a Realm object (UI thread or otherwise) unless it's in a write transaction. That being said, you could encapsulate that logic (i.e. open up a Realm write transaction on the current thread) pretty easily within that abstract object.
I have a class which as its properties has a number of NSSet values. The content of these sets is not stored in Core Data, but comes from another source. They are basically ID strings which are unique. No point in filling up Core Data with them as separate entities.
However, as far as I can see it's not possible to store an NSSet as an attribute of an NSManagedObject. I guess I would need to serialise it into binary data by hand, whenever the object gets stored, and deserialise it when it gets retrieved from the persistent store? The same would also apply to storing other collection classes.
Has anybody else ever come across this issue and is able to give some advice?
You can set the attribute type to 'transformable' and then directly store the set into the attribute. Be sure that all of the contents of the set conform to < NSCoding >.
In Xcode, in the Project Navigator open up the project_name.xcdatamodeld source for the model editor and select the entity in question. Choose Editor in the Xcode menu bar and there is a Create NSManagedObject subclass... item. Make the subclass and use it for extra non-Core Data properties. The subclass becomes part of the model in place of the entity. It retains all of the entity's behaviors.
I am quite new to MVC. I am having the following questions, please help me clarify those questions, thanks a lot.
Q1. Where should I populate my view model? e.g. My view model is very big, and contains a lot of the dropdown listbox, multi select list and the other few complex objects. I am currently populate them inside the view model self, by passing the model through the constructor, and load all the object data within the constructor. I also see my college populate the viewmodel inside controller. so I am confusing now, coz lots of people suggest to keep the controller small and skinny.
Q2. We are using the linq2sql as data access layer, should I use the table entity in my viewmodel instead of creating separate model class? Expert said it's bad, but if create seperate model class, I basically repeat them and I also need to copy those value to linq 2sql entity every time when I want to persist data, it's not that fun, too much work.
lots of people suggest to keep the controller small and skinny.
Yes. What people mean is that the controller should not contain any logic, except for model <-> view mapping. With model I mean the "M" in MVC.
Q2. We are using the linq2sql as data access layer, should I use the table entity in my viewmodel instead of creating separate model class? Expert said it's bad, but if create seperate model class, I basically repeat them and I also need to copy those value to linq 2sql entity every time when I want to persist data, it's not that fun, too much work.
No. You should not. Read my answer here: ASP.NET MVC Where to put custom validation attributes
Use a mapping framework for the model -> viewmodel conversion.
Update:
From what I understand, you suggest to assembly the viewmodel inside the controller (I mean call the business layer or repository to get my data) and use controller to call the business logic dealing with the data, am I right?
yes. The controller is really a glue between your business layer / repositories and your views. The views (and view models) should know nothing about them and the business layer / repositories should know nothing about the controller/view.
The controller was created for just that purpose. To create an abstraction between the user interface layer and the lower layers. Hence the only code that should exist in the controller is to make that possible (and therefore following the Single Responsibility Principle)
If you start adding that logic into your view models you start to add coupling between the lower layers and the user interface layer. Doing any changes in the lower layers will therefore also affect all your view models (instead of just the controller
your viewmodel should be a poco, and should most certainly not be handling the mapping of the model. if you don't want to map your model to your viewmodel in the controller i would suggest you look at something like automapper. it makes it easy.
once you've mapped from your model to your viewmodel, any extra properties that need to be set such as your lists should be set in the controller.
as i stated previously, definitely don't tie your viewmodel to your current orm or table structure. you never know what might need to be refactored, and if you can handle it via an automapper mapping instead of changing your view and viewmodel then you've saved yourself a significant amount of time.
Prior art which doesn't quite address this:
Core Data Migration error message "'Model does not contain configuration 'XYZ'.'"
I have narrowed this down to a specific issue. It takes a minute to set up, though; please bear with me.
The gist of the issue is that a persistentStoreCoordinator (apparently) cannot preserve the part of an object graph where a managedObject is marked as a subentity of another when they are stored in different files. Here goes...
1) I have 2 xcdatamodel files, each containing a single entity. In runtime, when the managed object model is constructed, I manually define one entity as subentity of another using setSubentities:. This is because defining subentities across multiple files in the editor is not supported yet. I then return the complete model with modelByMergingModels.
//Works!
[mainEntity setSubentities:canvasEntities];
NSLog(#"confirm %# is super for %#", [[[canvasEntities lastObject] superentity] name], [[canvasEntities lastObject] name]);
//Output: "confirm Note is super for Browser"
2) I have modified the persistentStoreCoordinator method so that it sets a different store for each entity. Technically, it uses configurations, and each entity has one and only one configuration defined.
//Also works!
for ( NSString *configName in [[HACanvasPluginManager shared].registeredCanvasTypes valueForKey:#"viewControllerClassName"] ) {
storeUrl = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:[configName stringByAppendingPathExtension:#"sqlite"]]];
//NSLog(#"entities for configuration '%#': %#", configName, [[[self managedObjectModel] entitiesForConfiguration:configName] valueForKey:#"name"]);
//Output: "entities for configuration 'HATextCanvasController': (Note)"
//Output: "entities for configuration 'HAWebCanvasController': (Browser)"
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:configName URL:storeUrl options:options error:&error])
//etc
3) I have a fetchRequest set for the parent entity, with setIncludesSubentities: and setAffectedStores: just to be sure we get both 1) and 2) covered. When inserting objects of either entity, they both are added to the context and they both are fetched by the fetchedResultsController and displayed in the tableView as expected.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesSubentities:YES]; //NECESSARY to fetch all canvas types
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20]; // Set the batch size to a suitable number.
[fetchRequest setAffectedStores:[[managedObjectContext persistentStoreCoordinator] persistentStores]];
[fetchRequest setReturnsObjectsAsFaults:NO];
Here is where it starts misbehaving: after closing and relaunching the app, ONLY THE PARENT ENTITY is fetched.
If I change the entity of the request using setEntity: to the entity for 'Note', all notes are fetched. If I change it to the entity for 'Browser', all the browsers are fetched. Let me reiterate that during the run in which an object is first inserted into the context, it will appear in the list. It is only after save and relaunch that a fetch request fails to traverse the hierarchy.
Therefore, I can only conclude that it is the storage of the inheritance that is the problem. Let's recap why:
- Both entities can be created, inserted into the context, and viewed, so the model is working
- Both entities can be fetched with a single request, so the inheritance is working
- I can confirm that the files are being stored separately and objects are going into their appropriate stores, so saving is working
- Launching the app with either entity set for the request works, so retrieval from the store is working
- This also means that traversing different stores with the request is working
- By using a single store instead of multiple, the problem goes away completely, so creating, storing, fetching, viewing etc is working correctly.
This leaves only one culprit (to my mind): the inheritance I'm setting with setSubentities: is effective only for objects creating during the session.
Either objects/entities are being stored stripped of the inheritance info, or entity inheritance as defined programmatically only applies to new instances, or both. Either of these is unacceptable. Either it's a bug or I am way, way off course.
I have been at this every which way for two days; any insight is greatly appreciated. The current workaround - just using a single store - works completely, except it won't be future-proof in the event that I remove one of the models from the app etc. It also boggles the mind because I can't see why you would have all this infrastructure for storing across multiple stores and for setting affected stores in fetch requests if it by core definition (of setSubentities:) doesn't work.
The answer is unfortunately easy. Subentities across physically different files and/or models is not supported. In the underlying data structure, Core Data will take all sub-entities and flatten them out to a single table. So if you have a Parent, ChildA, ChildB and ChildC each with 4 attributes, then you will end up with a single table with 16 columns.
This is why sub-entities are not supported in the manner that you are attempting. Each model is a silo unto itself and can at most have weak references to objects in the other model(s).
Update
It is an issue of "not supported by design" as I explained above. What you are trying to do is not supported because of the way it is persisted in the underlying data structure. Creating sub-entities is not something you should be doing a lot of in the first place because it flattens your data model.
Entity inheritance does not equal object inheritance. Your objects can inherit in any way, shape or form you want. Entity inheritance should be extremely rare and have a very good reason behind it.
Solving parent child relationship issues is one of the few reasons to use entity inheritance.
Trying to avoid duplication of columns is not a good reason.
Since you cannot do parent/child relationships across models (and you do have more than one model even if they are being merged into one instance of the NSManagedObjectModel) it is unlikely to be a reason to do entity inheritance across models that can't be solved via a similar solution.
#SG
You ask:
Why are there all these hooks for reading in multiple models, and saving to multiple persistent stores...
You can have multiple models and save them to different persistent stores because you would like to work on different parts of your xcdatamodel, e.g. editing attributes of EntityA effecting relationships to EntityB while reordering a huge amount of managedObjects of EntityB.
You could also perform better I/O streaming in another thread while reading in new objects, after this work is done, you could 'merge' them with existing data of your model.
This is a question for Objective-J/Cappuccino, but I added the cocoa tag since the frameworks are so similar.
One of the downsides of Cappuccino is that CoreData hasn't been ported over yet, so you have to make all your model objects manually.
In CoreData, your inverse relationships get managed automatically for you... if you add an object to a to-many relationship in another object, you can traverse the graph in both directions.
Without CoreData, is there any clean way to setup those inverse relationships automatically?
For a more concrete example, let's take the typical Department and Employees example. To use rails terminology, a Department object has-many Employees, and an Employee belongs-to a Department.
So our Department model has an NSMutableSet (or CPMutableSet ) "employees" that contains a set of Employees, and our Employee model has a variable "department" that points back to the Department model that owns it.
Is there an easy way to make it so that, when I add a new Employee model into the set, the inverse relationship (employee.department) automatically gets set? Or the reverse: If I set the department model of an employee, then it automatically gets added to that department's employee set?
Right know I'm making an object, "ValidatedModel" that all my models subclass, which adds a few methods that setup the inverse relationships, using KVO. But I'm afraid that I'm doing a lot of pointless work, and that there's already an easier way to do this.
Can someone put my concerns to rest?
I can't speak specifically to Objective-J, but the usual way of doing this without Core Data is to set the inverse relationship in the setter. So, using the employees/departments example, you would do something like this:
- (void)setDepartment:(Department *)aDepartment {
if (department == aDepartment)
return;
[department release];
department = [aDepartment retain];
[department addEmployee:self];
}
You need to make sure you don't update your instance variable if the new value already matches the existing value. If you didn't, setDepartment: would call addEmployee:, and addEmployee: would call setDepartment: in an infinite loop.
Also, note that this is an open invitation for retain cycles. It depends on how your model is structured, but the object that "owns" the the other is the one that should retain it. So my example is maybe not the best, because it's probably more accurate to say that the department "owns" the employee.
You probably want to set the relationship in your setter. Using your example the Objective-J code would look similar to this.
- (void)setDepartment:(Department)aDepartment {
if (department === aDepartment)
return;
[department addEmployee:self];
}
As you can see there is no need for retain / release. Objective-J is built upon javascript which is garbage collected. All the memory management methods are implemented but do nothing (apart from cluttering your code)
Also because this is javascript it's generally advisable to check for type equality (===) For more information on type equality see: http://www.webreference.com/js/column26/stricteq.html
check out the Cappuccino Extensions from this 280 North employee: http://github.com/nciagra/Cappuccino-Extensions
It includes an ActiveRecord port. I haven't actually looked at any of this up close yet, but it might help you.
Johannes
You can also check out this implementation of CoreData by rbartolome. I've only looked at it a little bit, but it looks like a start.
http://github.com/rbartolome/CoreData-Cappuccino