2
votes

I'm trying to implement a getter method for a simple transient property. The transient property is a fullName property. Typical example of fullName = firstName + lastName.

I'm developing an iOS app (just in case something related works differently on OS X)

Following the 'Mastering Core Data' WWDC 2010 Keynote I've created a category for my Person NSManagedObject subclass. In that category I've added the following method:

- (NSString *)fullName {

  [self willAccessValueForKey:@"fullName"];
  NSString *fullName = [self primitiveFullName];
  [self didACCessValueForKey:@"fullName"];

  if (fullName == nil) {
    fullName = [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
    [self setPrimitiveFullName:fullName];
  }
  return fullName;}

Person class has been created automatically by Xcode and has the fullName property and its implementation with @dynamic.

When I try to compile the project I get an error for this category saying "No visible @interface for 'Person' declares selector 'primitiveFullName'".

Why I'm getting this error when Apple documentation says "For example, given an entity with an attribute firstName, Core Data automatically generates firstName, setFirstName:, primitiveFirstName, and setPrimitiveFirstName:." ??

The error is a compile error, not a warning

Should I something special to have the primitive accessors generated?

2
In my opinion you should create subclass for Person class, and in that subclass override getter for fullName.Robert

2 Answers

2
votes

primitiveFullName and setPrimitiveFullName definitely will be generated automatically in runtime. But in compile time, compiler cannot find these methods. To suppress compile error when you invoke these methods, you should declare the prototype of these methods.

If when in 2010, Objective-C compiler said only warnings to invoke those undeclared methods. But after ARC introduced, Objective-C compiler disallow invoking such undeclared methods.

Like below:

@interface Person (PrimitiveAccessors)

- (NSString *)primitiveFullName;
- (void)setPrimitiveFullName:(NSString *)newName;

@end

@implementation Person (TransientProperties)

- (NSString *)fullName {
    [self willAccessValueForKey:@"fullName"];
    NSString *fullName = [self primitiveFullName];
    [self didAccessValueForKey:@"fullName"];

    if (fullName == nil) {
        fullName = [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
        [self setPrimitiveFullName:fullName];
    }
    return fullName;
}

@end
2
votes

First one comment on your design not an answer to the Q itself, but strongly related:

Your implementation is inconvenient: If you have a computed property, you should compute it. You should not store it. What happens, if firstName or lastName changed? Do you want to overwrite the setters for this properties, too, to nil fullName? If it is a computed property, compute it.

- (NSString*)fullName
{
  return [NSString stringWithFormat:@"%@ %@", [self valueForKey:@"firstName"], [self valueForKey:@"lastName"]];
}

Done.

This should solve your problem, because it is a different design. However, if you want to keep your old approach, you can use -primitiveValueForKey: and -setPrimitiveValue:forKey:.