Cocoa Programming for Mac OS X - Aaron Hillegass [122]
Figure 32.3. Department Entity
Note that the department relationship of Employee and the employees relationship of Department are inverses. Be sure that the inverse is set for both of these relationships (as is shown in the screenshots).
The manager relationship will not have an inverse. You may get a warning about this when you compile: Disregard the warning.
Create Custom NSManagedObject Classes
You will want to display an employee’s full name, so you are going to create a custom class to hold employee data. This class will be a subclass of NSManaged Object.
A department can be managed only by an employee who works for that department. When an employee who is the manager leaves the department, you will need to set the manager to nil. This will be handled by the custom NSManagedObject subclass for the Department entity.
Make sure that your MyDocument.xcdatamodel file is open in the editor, and select one of the entities. In Xcode, select the Editor -> Create NSManagedObject Subclass... menu item. Create classes for Employee and Department. (Figure 32.4)
Figure 32.4. Creating an NSManagedObject Subclass
Employee
In Employee.h, change firstName and lastName to be copy instead of retain, and declare the read-only property fullName:
#import #import @class Department; @interface Employee : NSManagedObject { @private } @property (nonatomic, copy) NSString *firstName; @property (nonatomic, copy) NSString *lastName; @property (nonatomic, retain) Department *department; @property (nonatomic, readonly) NSString *fullName; @end In Employee.m, implement the method fullName: - (NSString *)fullName { NSString *first = [self firstName]; NSString *last = [self lastName]; if (!first) return last; if (!last) return first; return [NSString stringWithFormat:@"%@ %@", first, last]; } We are going to bind the column of a table to the fullName key. If firstName or lastName is changed, it is important that observers of fullName get informed that it has also changed. You are going to override a class method to specify what keys cause changes in fullName: + (NSSet *)keyPathsForValuesAffectingFullName { return [NSSet setWithObjects:@"firstName", @"lastName", nil]; } Department #import "Department.h" #import "Employee.h" @implementation Department @dynamic deptName; @dynamic manager; @dynamic employees; - (void)addEmployeesObject:(Employee *)value { NSLog(@"Dept %@ adding employee %@", [self deptName], [value fullName]); NSSet *s = [NSSet setWithObject:value]; [self willChangeValueForKey:@"employees" withSetMutation:NSKeyValueUnionSetMutation usingObjects:s]; [[self primitiveValueForKey:@"employees"] addObject:value]; [self didChangeValueForKey:@"employees" withSetMutation:NSKeyValueUnionSetMutation usingObjects:s]; } - (void)removeEmployeesObject:(Employee *)value { NSLog(@"Dept %@ removing employee %@", [self deptName], [value fullName]); Employee *manager = [self manager]; if (manager == value) { [self setManager:nil]; } NSSet *s = [NSSet setWithObject:value]; [self willChangeValueForKey:@"employees" withSetMutation:NSKeyValueMinusSetMutation usingObjects:s]; [[self primitiveValueForKey:@"employees"] removeObject:value]; [self didChangeValueForKey:@"employees" withSetMutation:NSKeyValueMinusSetMutation usingObjects:s]; } @end Lay Out the Interface
In our model, a manager must be a member of a department. As such, if we are asked to remove an employee who is the manager of this department, we want to set the manager to nil. To enforce this, implement these methods in Department.m:
Before you begin editing the XIB files, a warning: There are a lot of bindings to make in this exercise. Be patient. Remember: In this book, you will never bind to a scroll view or a cell. You will, however, bind to table columns. Watch the title of the jump bar to be certain