andykkt
andykkt

Reputation: 1706

setPrimitiveValue for Transient property not saved

I am using some Transient property on my NSManagedObject to pre-calculate things that only need to be calculated once when dependent property is updated but when I reset the value on Transient property it never gets to saved.

For example:

-(NSString*)age {
    // notify core data to access value
    [self willAccessValueForKey:@"age"];
    // get primitive value
    NSString* age = [self primitiveValueForKey:@"age"];
    // is age already exist?
    if ( age == nil ) {
        // get age
        age = [GlobalHelper convertToAgeStringWithUTCDateOfBirth:(NSString*)self.dateOfBirth];
        // set primitive value
        [self setPrimitiveValue:age forKey:@"age"];
    }
    // notify core data done access value
    [self didAccessValueForKey:@"age"];
    return age;
}

-(void)setDateOfBirth:(id)dateOfBirth {
        // change value for key
        [self willChangeValueForKey:@"dateOfBirth"];
        [self willChangeValueForKey:@"age"];
        // set primitive
        [self setPrimitiveValue:dateOfBirth forKey:@"dateOfBirth"];
        [self setPrimitiveValue:nil forKey:@"age"];
        // did change value for key
        [self didChangeValueForKey:@"age"];
        [self didChangeValueForKey:@"dateOfBirth"];
}

So as you can see from above code, when I am sync this object and when "dateOfBirth" field is updated it try to set "age" to "nil" and when accessing "age" field and if it is "nil" then calculate the age and never calculate it again.

However the problem is when I am setting "age" field to "nil" in "setDateOfBirth" setting it never saved and when I am accessing age again "age" property still has the previous value but "dateOfBirth" is updated.

Any help remotely related to this topic would be very much appreciated, I am having big trouble about this...

EDIT: The logic I got above works perfect in scenario where both setter and getter calls from same context (same thread context), problem only occurs when setter called from completion block of AFNetworking api with private queue and getter called from main thread after merged.

It seems that when I am saving from the context where AFNetworking completion block it only saves the dateOfBirth but not age value which it sets to nil so after merge and when getter calls from the main thread "age" has previous value so it never get to calculated again.

Upvotes: 0

Views: 374

Answers (1)

Mundi
Mundi

Reputation: 80271

You should not set the age when reading the age. That seems very odd design and also fails to call the appropriate key-value coding notifications.

Instead follow your original logic: if date of birth changes, update age. You should do this in setDateOfBirth and call the appropriate key-value coding notifications in the setter for age.

-(void) setDateOfBirth:(id)newValue {
    [self willChangeValueForKey:@"dateOfBirth"];
    [self setPrimitiveValue:newValue forKey:@"dateOfBirth"];
    [self didChangeValueForKey:@"dateOfBirth"];

    self.age = [GlobalHelper 
       convertToAgeStringWithUTCDateOfBirth:(NSString*)newValue];
}

-(void)age {
  [self willAccessValueForKey:@"age"];
  NSString* age = [self primitiveValueForKey:@"age"];
  [self didAccessValueForKey:@"age"];
  if (age == nil) {
      age = [GlobalHelper 
         convertToAgeStringWithUTCDateOfBirth:(NSString*)self.dateOfBirth];
  }
  self.age = age;
  return age;
}

However, with all that you have to ask yourself how expensive the convertAge call really is. In all but the most extreme cases, one simple getter that makes the conversion is actually all you need.

Also, I think your dateOfBirth property should have a standard managed object property type, not id. Is it a string? Then you should declare it as NSString, not use id and cast.

Upvotes: 1

Related Questions