Cory
Cory

Reputation: 2312

CoreData attribute value based on other attributes

I have a number of attributes for a CorData entity that are based on the values of other attributes. For example transactionTotalValue = transactionPrice * transactionQuantity. Currently I subclassed the NSManagedObject and created custom setters like this

- (void)setTransactionQuantity:(NSDecimalNumber *)transactionQuantity
{
    [self willChangeValueForKey:@"transactionQuantity"];
    [self setPrimitiveValue:transactionQuantity forKey:@"transactionQuantity"];
    [self didChangeValueForKey:@"transactionQuantity"];
    [self updateTotalValue];
}

- (void)setTransactionPrice:(NSDecimalNumber *)transactionPrice
{
    [self willChangeValueForKey:@"transactionPrice"];
    [self setPrimitiveValue:transactionPrice forKey:@"transactionPrice"];
    [self didChangeValueForKey:@"transactionPrice"];
    [self updateTotalValue];
}

- (void)updateTotalValue
{
    self.transactionTotalValue = [self.transactionQuantity decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithDecimal:[self.transactionPrice decimalValue]]];
}

Is this an acceptable way of doing this? if not what would be considered best practice for this situation?

The other alternative is to use KVO as follows

- (NSDecimalNumber *)transactionTotalValue
{
    [self willAccessValueForKey:@"transactionTotalValue"];
    NSDecimalNumber *total = [self.transactionQuantity decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithDecimal:[self.transactionPrice decimalValue]]];
    [self didChangeValueForKey:@"transactionTotalValue"];
    return total;
}


+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
    NSSet *keypaths = [super keyPathsForValuesAffectingValueForKey:key];

    if ([key isEqualToString:@"transactionTotalValue"]) {
        NSArray *affectingKeys = @[@"transactionQuantity", @"transactionPrice"];
        keypaths = [keypaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keypaths;
}

Is this the better option?

Upvotes: 2

Views: 200

Answers (1)

mikeD
mikeD

Reputation: 195

The attribute transactionTotalValue is to be completely dependent upon the two other attributes. Declare it as a readonly, nonatomic property.

@property (nonatomic,readonly) id transactionTotalValue;

You can then implement the getter.

- (id)transactionTotalValue 
    {
        //check for non-existent needed properties and handle here

        return [self.transactionQuantity 
        decimalNumberByMultiplyingBy:[NSDecimalNumber 
        decimalNumberWithDecimal:[self.transactionPrice decimalValue]]];
    }

Also override the class method for keys that affect the dependent property.

+ (NSSet *)keyPathsForValuesAffectingTransactionTotalValue
    {
        return [NSSet setWithObjects:@"transactionQuantity", @"transactionPrice", nil];
    }

The transactionTotalValue property will be re-read as needed, say if you are updating a table source through bindings. By making it readonly you will be able to avoid any setter methods.

Upvotes: 1

Related Questions