So I have two objects, Invoice and InvoiceLineItem. InvoiceLineItem has a property called cost
and it is dynamically created based on other properties. To help with the KVO/bindings I use:
+ (NSSet *)keyPathsForValuesAffectingCost {
return [NSSet setWithObjects:@"lineItemType", @"serviceCost", @"hourlyRate", @"timeInSeconds", @"productCost", @"quantityOfProduct", @"mileageCost", @"milesTraveled", nil];
}
This works great. When I edit a property like serivceCost the main cost in the Table View updates fine.
In the Invoice object I have an NSMutableArray of InvoiceLineItems. Invoice has a similar property called totalCost
. It is calculated by iterating over the line items and as long as the line item isn't marked as deleted(which I do for syncing reasons) it adds up the costs and creates the totalCost.
Now my question/issue. How do I set up Invoice's totalCost so that it works with KVO/bindings when one of the line item's costs has changed?
I tried setting up:
+ (NSSet *)keyPathsForValuesAffectingTotalCost {
return [NSSet setWithObjects:@"lineItems.cost", nil];
}
but it doesn't work. I end up with an error in the console: [<NSCFArray 0x1499ff40> addObserver:forKeyPath:options:context:] is not supported. Key path: cost
I don't believe to-many relationships are supported for automatic KVO propogation. The documentation doesn't say explicity one way or the other, but from what I know of KVO in general, observing subkeys of a to-many relationship tends to be non-trivial.
The way I would approach this would be to manually observe the cost
property of each InvoiceLineItem object, by implementing the to-many KVC accessors for the lineItems
property on the Invoice class doing an addObserver/removeObserver call in the insert/remove methods, respectively, and then trigger the totalCost
change manually using willChangeValueForKey:/didChangeValueForKey:. So something like this (roughly sketched code, disclaimers etc.):
- (void)insertObject:(InvoiceLineItem*)newItem inLineItemsAtIndex:(unsigned)index
{
[newItem addObserver:newItem forKeyPath:@"cost" options:0 context:kLineItemContext];
[lineItems insertObject:newItem atIndex:index];
}
- (void)removeObjectFromLineItemsAtIndex:(unsigned)index
{
[[lineItems objectAtIndex:index] removeObserver:self forKeyPath:@"cost"];
[lineItems removeObjectAtIndex:index];
}
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
if (context == kLineItemContext)
{
[self willChangeValueForKey:@"totalCost"];
[self didChangeValueForKey:@"totalCost"];
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With