Quirk I just discovered, and wanted to confirm with anyone here whether or not this is avoidable. Basically, if I have a very simple two entity model:
With a to-many relationship between Entity1 and Entity2. The relationship is optional, with nullify as the delete rule on both sides. However, if I insert a new Entity1 the value of the children relationship will be an empty set, not nil:
NSManagedObject *object = [NSEntityDescription
insertNewObjectForEntityForName:#"Entity1"
inManagedObjectContext:[self managedObjectContext]];
assert([object valueForKey:#"children"] != nil);
Furthermore, I can't explicitly set the relationship to nil:
[object setValue:nil forKey:#"children"];
assert([object valueForKey:#"children"] != nil);
I have verified this in a new, minimal project and this appears to be a true implementation detail. The problem is, I would like to be able to differentiate between a nil value (representing currently unknown) and an empty set (truly a to-zero relationship). Does CoreData actually support this at all in a reasonably direct manner? Currently it seems not, which means I will have to find another (less direct) way to represent my model.
Thanks,
J
Short answer is no. You will always get an empty set back. If you need to know if an object structure is fully realized (my guess at what your goal is) then you would want to set an attribute on the object to say if it is fully realized or not.
Related
My NSFetchedResultsController work great, as long as only "basic" attributes get changed. However if I have a label which is calculated and I'm changing some attributes influencing this label in another view controller on the navigation controller stack, this label doesn't get updated.
For example my label should show the amount of a budget position left saved in the entity SpendingCategory.
self.budgetLeftLabel.text = [NSString stringWithFormat:#"%# %#", [[self.spendingCategory getExpendituresAmount] getLocalizedCurrencyStringWithDigits:0], NSLocalizedString(#"left", nil)];
I derive this value from the category on SpendingCategory with this method:
- (NSNumber *)getExpendituresAmount
{
return [self.hasExpenditures valueForKeyPath:#"#sum.amount"];
}
However this label doesn't get any updates by the NSFetchedResultsController. And I have several locations in my app where this doesn't happen because a value is calculated. What do I need to change that these updates happen?
EDIT with datastructure:
Ok my Spending Category datastructure is roughly (for budget):
name (string)
cost (double)
position (integer 16)
Relationsships: hasExpenditures
My Expenditures structure (for tracking):
amount (double)
date (Date)
description (string)
Relationsships: forSpendingCategory
I hope it's clearer now. So why do these values not get updated?
The NSFetchedResultsController gets tickled when attributes in the relevant NSManagedObject instances are updated. If you are changing something that is purely calculated then the update never fires. Why is this relevant?
If you are changing something in the Expenditures entity (btw, entities should be singular in name) and you are watching the Spending Category entity then the NSFetchedResultsController won't fire because you didn't change anything that is relevant.
How to fix this?
Depends. I normally keep that derived value in the entity and persist it. Further, whenever a child changes a relevant value, I have the parent recalculate. This will cause the NSFetchedResultsController to fire.
How do you watch the values?
Either you have the child call a method on the parent (icky) or you have the parent watch the values on its children via KVO (better). Your personal preference decides here.
Update 1
To keep the derived value in the entity you add a new attributed to the entity and store it. Nothing is special about the attribute. It helps to keep in mind that Core Data is not a database. Core Data is your data model that happens to persist to a database if you so choose. Therefore you want to denormalize the database in cases like this.
while I was searching SO to find a good link for watching children, I stumbled across this example.
KVO object properties within to-many relationship
While the accepted answer is not very good, the second answer, using a NSFetchedResultsController is quite interesting and is worth exploring. The basic idea is that your parent objects instantiate a NSFetchedResultsController on -awakeFromFetch or -awakeFromInsert and when it fires, they recalculate the derived value. Thus the value is always up to date and your view controller based NSFetchedResultController instances will fire because the parent object has changed.
I did something similiar time ago, basically you need to store your calculated value in a transient attribute in your CoreData model, rather than implement your own setter and getter. Then in the related NSManagedObject you need to implement two methods:
// this will populate the values when
// the entity is retrieved from the store
-(void)awakeFromFetch {
[self refreshCellInfo];
}
// this will refresh the values when
// the object goes to fault
// (for example when it is off screen)
-(void)willTurnIntoFault {
[self refreshCellInfo];
}
-(void)refreshCellInfo {
// update all your derived values...
}
I looked over the SO, but strangely didn't find similar question.
So the question is: If i have an entity called A and an entity called B, can B object have a property of type A? NOT relationship, but a property. I surely can use relationship as a property, but in my case i need an A object to have a property with type of A object. for example
Entity called Human. And a property called child which is a Human too.
Human* parent=[Nsentity....bla bla
Human* child=parent.child;
Is there a way to do this not using transformable properties? I tried non-inverse relationship to self, but it rises a warning, and im like afraid that its gonna be a mess after all. cos in my case "parent" and "child" might be exactly the same object. so parent.child might be equal to parent
The is no prevention for "self" relationships.
An entity A may have a relation (one-to-one or one-to-many) with entity A or any of its descendants (or any other entity for that matter).
Define A like so (for one-to-one relationship):
parent (reltionship with inverse A->child)
child (relationship with inverse A->parent)
You probably don't want this to be a property as CoreData will not maintain it as part of the object graph (cascade rules and such).
What method will be called by Core Data on NSManagedObject when setting a value for to-one relationship? What method will be called when adding a value to to-many relationship? Thanks.
/Mikael
Maybe you are looking for something like:
Managed Object Accessor Methods - Core Data Programming Guide
Its like this
managedObjectInstance.property = value;
[self.managedObjectContext save:&error];
Ex: if you want to save username in user entity it will be
_userEntity.username = #"Mikeal Hakman";
[self.managedObjectContext save:&error];
One to Many or Many to May always carries NSSet example
_residenceEntity.Seller = [NSSet setWithObjects:seller, nil];
[self.managedObjectContext save:&error];
Obviously I didn't manage to formulate my question clearly enough. I'll try again.
In a subclass of NSManagedObject I need to know when to-many and to-one relationships are being changed. That includes the very first change when the object is being fetched or inserted. I try all the accessors described in https://developer.apple.com/library/mac/documentation/cocoa/conceptual/coredata/articles/cdAccessorMethods.html to no avail. I can see in my UI that the relationship is there but no accessor methods has been called on my object. Also when I remove the relationship, it goes away in UI but no methods on my object are called. Thanks.
/Mikael
I am trying to do exactly same thing as post in NSFetchResultsController + sectionNameKeyPath + section order, i.e. basically use 2 tables, let's say Categories <-->> Events. Category table consists of category field only, while Event consists of name, dateTimestamp.
I defined relationship 'category' in Events table and try to use that relationship as sectionNameKeyPath when creating fetchedResultsController:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"category.category" cacheName:#"Root"];
Finally, I pre-populated Category table with some categories upon loading of the app (and verified with .dump that table is populated correctly)
Yet, I simulator fails on:
return [[self.fetchedResultsController sections] count];
I did extensive search and most people either suggest using one of the fields in the table as sectionNameKeyPath (this works!) or transient property (works too!) However, I just want to use relationship as it seems very logical to me in this case where events belong to some categories and there could be categories without events. Am I wrong in my assumption that relationship can be used as sectionNameKeyPath? The original link at the top of the question suggests it works, but guy does not know why or how. Documentation is very weak on what can be used as sectionNameKeyPath, so any help will be highly appreciated.
A relationship gets you a pointer to a managed object. It seems logical, though, that the sectionNameKeyPath parameter should be a key path that leads to a string, since NSFetchedResultsSectionInfo's name property is a string. The fetched results controller will follow that key path for each fetched object and group the objects into sections based on what they return for that key path, and it'll also use those strings as the names of their respective sections. You can't use a managed object for the name -- you have to use some string property of the managed object.
So, your Category entity must have an attribute that distinguishes one category from another, right? Use that as the key path and (as you've seen) everything will work out.
BTW, I think it's useful to try to get out of the database (rows/fields) mindset and try to think in object-oriented terms like entity and attribute. A big selling point of Core Data is that it provides an abstraction layer that hides the storage mechanism. Thinking in terms of tables is like thinking about blocks and sectors when you're reading or writing a file.
Caleb, thank you for your answer. I do believe my understanding was wrong to some degree. What I had was an entity Category and entity Event. Category has a string field 'category', thus 'category.category' path (first 'category' is relationship in the Event entity)
What I did not take in account, though, is that if there are no events, fetchresultscontroller cannot fetch anything (similar to 'left join')
What I wanted is to show categories even if there are no events. Relationship 'category' will not return anything in this case as there is nothing to return/sort/categorize.
What I had to do (wrong or right - not sure yet) is to treat [managed] object created from Category entity as a separate object in case there are no events and place in the table. When there is one event per category, I can switch to the original method of [automatic] showing events sorted by categories.
This is interesting issue of starting point (empty entities with relationships) where I feel core data is more confusing than traditional relationship database. I also believe that's why all books/articles/reports carefully stay away from this topic. In other words, I could not find analog of "left join" in core data. May be I am wrong because I am relatively new to all this. Below is the description of the entities:
Category <-->> Event
Category - parent
Category.category - attribute of type String
Category.event - relationship to Event entity
Event - child
Event.name - attribute of type String
Event.category - relationship to Category entity
Each event belongs to one category. Category may have multiple events.
Categories should be shown even if there are no events for this category.
I was trying to put Events under fetchresultscontroller. May be I should switch to Category first and then calculate cell based on category.event relationship, not the other way around - did not try that yet.
I've got a few questions I've been trying to answer for myself (by hunting through the documentation) but I have a feeling I'm missing something.
Any hints (and/or pointers to appropriate documentation) would be much appreciated.
I'm building a Core Data document-based application. There are essentially two entities:
There is a single "Comparison" record associated with each document.
There are potentially many "Node" records associated with each document.
My first question is whether I'm thinking about this correctly. Since there is only a single Comparison object for each document, the attributes of the Comparison are essentially attributes of the Document itself. What (if any) is the preferred way of modeling that?
If a Comparison entity is in fact the right way to go, my next question is how and when to actually instantiate the (single) Comparison object. The user should not have to explicitly "add" the Comparison since there's going to be only one of them associated with the Document. Instead, a single Comparison object should be instantiated and inserted into the managedObjectContext. I've got something like this working already, with code in MyDocument.m that looks like this:
(void)windowControllerDidLoadNib:(NSWindowController *)windowController {
[super windowControllerDidLoadNib:windowController];
[NSEntityDescription insertNewObjectForEntityForName:#"Comparison" inManagedObjectContext:managedObjectContext];
}
However -- if the user creates a new document but then never does any work with it -- for example if he immediately clicks the close button -- then he should not be asked to "Save" the document. He should be asked to save his work only if he's actually entered any information. Is there a preferred way to implement this behavior?
I found this thread while struggling with the exact same issue. I have a table of Entity_A working in my document based Core Data app, but I need to figure out how to handle a required single-instance per document of Entity_B.
I've found something that seems to work. There's probably a better way, but this is getting me past this hurdle for now.
When the document's xib is loaded I simply check to see if an Entity_B has been created. if not, I create one and initialize its attributes.
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
//has an Entity_B been created? if not, create one.
NSError *theError = nil;
NSUInteger count = [[self managedObjectContext] countForFetchRequest:[NSFetchRequest fetchRequestWithEntityName:#"Entity_B"] error:&theError];
if( count == 0 )
{
NSManagedObject *newEntity_B = [NSEntityDescription insertNewObjectForEntityForName:#"Entity_B" inManagedObjectContext:[self managedObjectContext]];
[newEntity_B setValue:[NSNumber numberWithBool:YES] forKey:#"boolAttribute"];
[newEntity_B setValue:[NSNumber numberWithInt:2] forKey:#"intAttribute"];
}
}
I didn't insert that code snippet into the original post correctly. Trying again:
-(void)windowControllerDidLoadNib:(NSWindowController *)windowController {
[super windowControllerDidLoadNib:windowController];
[NSEntityDescription insertNewObjectForEntityForName:#"Comparison" inManagedObjectContext:managedObjectContext];
}
Your question about modelling is not very clear, can you please elaborate on what your "Comparison" entity is supposed to do and what sort of attributes you are assigning to it? It would be handy to see your "Document" entity structure so we can provide some useful input.
With regards to your second question, you could check if your NSManagedObject has been updated before deciding on whether to prompt the user to save their document or not:
if ([documentObject isUpdated]) {
...
}
More details in the documentation here http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObject_Class/Reference/NSManagedObject.html#//apple_ref/occ/cl/NSManagedObject
Cheers,
Rog
There isn't really a "Document" entity, I was simply using that term to refer to the overall document that is saved when the user invokes the Save menu item. Perhaps there is a better way to refer to this concept? NSPersistentDocument?
Backing up a bit... the central idea of the application is to compare two hierarchical directory structures (a visual recursive "diff").
For now the "Comparison" entity has two string attributes, pathA and pathB, which are the names of the two directories to be compared. Each "Node" entity represents the name of a file down in the directory trees that are being compared. The Node entity contains at least one attribute ("relativePath") which is the path relative to the starting point specified in the Comparison.
My first question was simply whether it makes sense for there to be a "Comparison" entity since there is going to be only one of them instantiated (at some point after the user invokes the "New" menu item).
The second question is really at what point should the single "Comparison" object be instantiated and inserted into the managedObjectContext, i.e. what method is most appropriate to make this happen?
Finally if a "Comparison" object is automatically instantiated (at awakeFromNib time, perhaps?) but the user decides not to proceed, and simply clicks the close button, he should not be prompted to save (right?) What would be the appropriate way to accomplish this? The documentObject will appear to have been updated, because an "empty" Comparison object has in fact already been inserted automatically at startup, but the user has not modified it.
Hope that's clear... thanks.