I recently realized that NSManagedObject subclasses inherit a class method entity which can be used to obtain a NSEntityDescription for the class. However, I was used to having to specify a context when creating a NSEntityDescription, as with entityForName:inManagedObjectContext:. Is it ok to use the simpler entity method and what context will it be associated with ?
This method is not really documented by Apple.
An NSEntityDescription is not part of a managed object context-- it's part of the managed object model.
When you load a data model, all of the entity descriptions it contains are loaded. The class method +entity works because the entity description was created along with the model object. If you try to call this method before loading the model, it returns nil in Objective-C. (In Swift for some reason it returns a non-optional value, so it's not nil, but if you use it your app will crash. Don't ask me why it's like this.)
You can also use +entityForName:inManagedObjectContext:, as you mentioned. But look at the documentation for that method:
Returns the entity with the specified name from the managed object model associated with the specified managed object context’s persistent store coordinator.
So even though the method takes a managed object context argument, it's still using the managed object model. It's using the context to find the model. The object that you get isn't associated with the context, it's associated with the underlying data model.
These two methods are equally safe. Use whichever works best in your code.
Related
I am trying to add a new entity in NSManagedObjectModel in my NSIncrementalStore Subclass. I am doing this in loadMetadata method but it keeps throwing this exception on the last line. See Code Below
"NSInternalInconsistencyException" Entities for a configuration must already be in the model
Code
var model:AnyObject=(self.persistentStoreCoordinator?.managedObjectModel.copy())!
var newEntity=NSEntityDescription()
newEntity.name="newEntity"
newEntity.managedObjectClassName="newEntity"
var entities=model.entitiesForConfiguration(self.configurationName)
entities?.append(newEntity)
model.setEntities(entities!, forConfiguration: self.configurationName)
You cannot modify a model after it has been added to a persistent store coordinator. The only time you can manipulate the model is just after initialization and before applying it to the NSPersistentStoreCoordinator.
The documentation is unclear about this, but before calling setEntities:forConfiguration: the entities being set must already exist in the managed object model's entities array. This is because this method actually assigns existing entities in the model to a particular configuration.
The solution here is to make a mutable copy of the entities array, add your entities to it if they do not exist, and then set the managed object model's entities array to an immutable copy of the modified array.
After that point you can invoke setEntities:forConfiguration:.
It would be worth filing a radar bug report on this behavior.
I am learning a bit on NSCoreData and before introducing it some existing projects I have, I would like to validate my good understanding of the core principles.
From what I have understood, NSCoreData make it easier to manage local storage of object (+retrieval after that) by subclassing our Model class from NSManagedObject rather than from NSObject.
Right ?
I have a few questions then. Let's consider I am building a real estate application with as core model object the class Property that can represent an appartment, a house, and all related information. Currently it is managed in my app as a subclass of NSObject.
1) I retrieve the properties from the server through a search query, and have written a initWithJson : method to populate each instance.
Now if I subclass Property from NSManagedObject, I will create my instances by using
+(id)insertNewObjectForEntityForName:(NSString *)entityName
inManagedObjectContext:(NSManagedObjectContext *)context
and I will be still be able to add a populateWithJson: to my class to fill in the properties.
Then I will create a lot of Property instances in the current managedObjectContext, and if I do a save, they will be stored at the physical layer.
If I call again the same webservice, and retrieve the same JSON content, I will recreate the identical managed objects.
How to avoid redundancy with the [managedObjectContext save:&error] call and not to store physically several time the representation of a single real life property ?
2) Let's say I want to store physically only some properties, for instance only the one the user want to have as favorites.
[managedObjectContext save:&error] will save all created / modified / deleted managed objects from the context to the physical layer, and not only the one I want.
How to achieve that ?
Am I supposed to declare another context (managedObjectContext2), move the instance I want to store in that context, and do the save in that one ?
(I mean, I will have a context just to manipulate the object, create instances from the JSON and represents them in UI ... and a second one to actually do the storage)
Or am I supposed to stores all the objects, and add a isFavorite BOOL property , and then fetching using a predicate on that property ?
3) The app has a common navigation pattern : the UITableView lists Properties instance with the minimum information required, and going on a detail view call a webservice to request more information on a specific Property instance (images, full text description).
Is it a good practice for instance to call the webservice only if the property.fullDescription is nil, and then update the object and store it locally with all detailed information, and the next time only to fetch it locally with a predicate on the property.id ?
What about object that might be updated server-side after they have been created?
Thanks for your lights
1) Retrieve the server data into a temporary form (array of dictionaries?), then for each possible property in the array, check to see if you already have an object in Core Data that matches. If you do, either ignore it or update any changed attributes; if not, create a Property object.
2) Decide which things you want to persist in order to support your app's functions. There's no point in creating a managed object for something you don't want to save. Note, though, that Core Data supports sub-classes if you want both Property and FavoriteProperty.
3) Entirely up to your "business rules"…. How often do you need local data to be updated? The only technical consideration might be the guideline to not keep large files locally that can be re-created on demand.
I have NSObject class name TrackInfo which contains tracks info like name , artist name,thumb image etc.
I use this class as downloading data and save information to that class after parsing data.
Now I have another tab in which, I have to show some data. This is same kind of data like trackInfo. But when app is in OFFLINE, I have to make NSManagedObject. It is same as trackinfo.
Can I use NSObject class instead of NSManagedObject or Vice-Versa ?
What I basically wants to do is, I have to display track info from one class either Trackinfo (NSObject class) or NSManagedObjectClass which is used to save data when app is in offline.
Short answer is yes, you can. How? You can find a useful discussion Organising Core Data for iOS.
The long answer can be grabbed within the documentation.
NSManagedObject is a generic class that implements all the basic
behavior required of a Core Data model object. It is not possible to
use instances of direct subclasses of NSObject (or any other class not
inheriting from NSManagedObject) with a managed object context. You
may create custom subclasses of NSManagedObject, although this is not
always required. If no custom logic is needed, a complete object graph
can be formed with NSManagedObject instances.
A managed object is associated with an entity description (an instance
of NSEntityDescription) that provides metadata about the object
(including the name of the entity that the object represents and the
names of its attributes and relationships) and with a managed object
context that tracks changes to the object graph. It is important that
a managed object is properly configured for use with Core Data. If you
instantiate a managed object directly, you must call the designated
initializer (initWithEntity:insertIntoManagedObjectContext:).
About your question, it depends on what you need to achieve. If your goal is to perform a sync mechanism between your device and the server, you should set up 1) a model with a TrackInfo entity 2) a Core Data stack that relies on a persistent store like SQLite. Then you should modify TrackInfo to take into account modifications to that entity. For example, a dirty flag property (0 or 1) or a timestamp. When you do a modification on your TrackInfo you update that property. When the connection is restored you need to query against that property and sync with the server. If you choose the timestamp, the server should say what is the latest timestamp to query against.
After reading RestKit docs about RKManagedObjectStore I was confused about createPersistentStoreCoordinator method because there is a next warning in the description of this method:
**Warning:** Creating the persistent store coordinator will
render the managed object model immutable. Attempts to
use functionality that requires a mutable managed object model
after the persistent store coordinator has been created
will raise an application error.
I didn't understant what does it mean immutable managed object model? I can't found any information about this topic neither in official Core Data docs nor accross the Internet. Can someone give me an explanation of the difference between mutable and immutable managed object models? Why does creating of persistent store coordinator renders immutable managed object model? And what functionality requires a mutable managed object model?
Thanks in advance.
You can change a NSManagedObjectModel (add entities and attributes for example) in code, it is said to be mutable. But once you attach your object model to an persistent store coordinator you are not allowed to change it anymore - it has become immutable.
This also is described in the documentation for NSManagedObjectModel:
Editing Models Programmatically Managed object models are editable
until they are used by an object graph manager (a managed object
context or a persistent store coordinator). This allows you to create
or modify them dynamically. However, once a model is being used, it
must not be changed. This is enforced at runtime—when the object
manager first fetches data using a model, the whole of that model
becomes uneditable. Any attempt to mutate a model or any of its
sub-objects after that point causes an exception to be thrown. If you
need to modify a model that is in use, create a copy, modify the copy,
and then discard the objects with the old model.
This is a simplified version of my scenario, but that's OK.
Let's say you have three CoreData entities, each with a corresponding subclass of NSManagedObject, and 1-to-1 relationships as follows:
Person -> ContactRecord -> PhoneNumber
The Person entity has a vcard attribute, which holds the vCard data for that person in a string.
The PhoneNumber entity has two attributes: the actual number, and the type of phone number (cell, home, work, etc).
Right now, in the Person willSave method, I'm updating the vcard property. This works fine, if another property on the Person object has changed. But, if I change the type or number on the PhoneNumber object, or any properties on the ContactRecord, the willSave method isn't called on the Person object.
Is there a good way to update the Person object when a property changes over on the PhoneNumber object?
Right now, the best option I see is using NSManagedObjectContextWillSaveNotification. The method called by that notification could sift through the changed objects and work back up the inverse relationships to call some method on the Person object, but this happens after the NSManagedObjectContext has already saved, so it would require another save after that. My hope was to set this property to the correct value before the save happens.
The NSManagedObjectContextWillSaveNotification method is called just before the save occurs, so you could make the change there and it should not need a second save.
My preference in a situation like this would be to not store the vcard at all, but make it a dependent property that is readonly. I assume you don't need to access the vcard very often, so generating the data on-the-fly in the getter method should work OK, and the data will always be up-to-date.
One problem with updating the data just before a save is that you have to make sure you save. If you try to get the vcard without saving, it will be out-of-date. The dependent read-only property won't have that issue.
A straight-forward way is to continue to use the -willSave: method in the children. When the -willSave: fires on ContactRecord then ping the parent and ask it to recalculate the vcard. You can do the same thing for PhoneNumber. Just work back up the relationships.
This is assuming your relationships are bi-directional which, while flagged as a warning, really is a requirement with Core Data.