Changes made to objects’ property values should be propagated to the user interface, and user interface elements displaying the same property should be kept synchronized. Cocoa bindings provides a control layer for Cocoa but, whereas the Core Data framework focuses on the model, Cocoa bindings focus on the user interface. In many situations, Cocoa bindings makes it easy to keep the user interface properly synchronized. The Core Data framework is designed to interoperate seamlessly with, and enhance the utility of, Cocoa bindings.
Cocoa bindings and Core Data are largely orthogonal. In general, Cocoa bindings work in exactly the same way with managed objects as with other Cocoa model objects. You can also use the same predicate objects and sort descriptors as you use to fetch objects from the persistent store to filter and sort objects in memory—for example to present in a table view. This gives you a consistent API set to use throughout your application. There, however, are a few (typically self-evident) differences in configuration and operation.
In addition to the issues described in this article, there are a few other areas where the interaction between Core Data and Cocoa Bindings may cause problems; these are described in “Troubleshooting Core Data,” in particular:
“Custom relationship set mutator methods are not invoked by an array controller”
“Cannot access contents of an object controller after a nib is loaded”
Modulo these exceptions, everything that is discussed and described in Cocoa Bindings Programming Topics applies equally to Core Data-based applications and you should use the same techniques for configuring and debugging bindings when using Core Data as you would if you were not using Core Data.
Additions to Controllers
Automatically Prepares Content Flag
Entity Inheritance
Filter Predicate for a To-many Relationship
The main area where Core Data adds to Cocoa bindings is in the configuration of the controller objects such as NSObjectController
and NSArrayController
. Core Data adds the following features to those classes:
A reference to a managed object context that is used for all fetches, insertions, and deletions.
If a controller's content is a managed object or collection of managed objects, you must either bind or set the managed object context for the controller.
An entity name that is used instead of the content object class to create new objects
A reference to a fetch predicate that constrains what is fetched to set the content if the content is not set directly
A content binding option ("Deletes Objects On Remove") that—if the content is bound to a relationship—specifies whether objects removed from the controller are deleted in addition to being removed from the relationship
If the "automatically prepares content" flag (see, for example, setAutomaticallyPreparesContent:
) is set for a controller, the controller's initial content is fetched from its managed object context using the controller's current fetch predicate. It is important to note that the controller's fetch is executed as a delayed operation performed after its managed object context is set (by nib loading)—this therefore happens after awakeFromNib
and windowControllerDidLoadNib:
. This can create a problem if you want to perform an operation with the contents of an object controller in either of these methods, since the controller's content is nil
. You can work around this by executing the fetch "manually" with fetchWithRequest:merge:error:
. You pass nil
as the fetch request argument to use the default request, as illustrated in the following code fragment.
- (void)windowControllerDidLoadNib:(NSWindowController *) windowController |
{ |
[super windowControllerDidLoadNib:windowController]; |
NSError *error; |
BOOL ok = [arrayController fetchWithRequest:nil merge:NO error:&error]; |
// ... |
If you specify a super entity as the entity for a fetch request, the fetch returns matching instances of the entity and sub-entities (see “Fetching and Entity Inheritance”). As a corollary, if you specify a super entity as the entity for a controller, it fetches matching instances of the entity and any sub-entities. If you specify an abstract super-entity, the controller fetches matching instances of concrete sub-entities.
Sometimes you may want to set up a filter predicate for a search field that lets a user filter the contents of an array controller based on the destination of a to-many relationship. If you want to search a to-many relationship, you need to use an ANY
or ALL
in the predicate. For instance, if you want to fetch Departments in which at least one of the employees has the first name "Matthew", you use an ANY
operator as shown in the following example:
NSPredicate *predicate = [NSPredicate predicateWithFormat: |
@"ANY employees.firstName like 'Matthew'"]; |
You use the same syntax in a search field's predicate binding:
ANY employees.firstName like $value |
Note: You cannot use the contains
operator (for example, ANY employees.firstName contains 'Matthew'
) because the contains
operator does not work with the ANY
operator.
Things are more complex, however, if you want to match prefix and/or suffix—for instance, if you want to look for Departments in which at least one of the employees has the first name “Matt”, “Matthew”, “Mattie”, or any other name beginning with “Matt”. Fundamentally you simply need to add wildcard matching:
NSPredicate *predicate = [NSPredicate predicateWithFormat: |
@"ANY employees.firstName like 'Matt*'"]; |
You cannot, though, use the same syntax within a search field's predicate binding:
// does not work |
ANY employees.firstName like '$value*' |
The reasons for this are described in Predicate Programming Guide—putting quotes in the predicate format prevents the variable substitution from happening. Instead, you must use substitute any wildcards first as illustrated in this example:
NSString *value = @"Matt"; |
NSString *wildcardedString = [NSString stringWithFormat:@"%@*", value]; |
[[NSPredicate predicateWithFormat:@"ANY employees.firstName like %@", wildcardedString]; |
By implication, therefore, you must write some code to support this behavior.
Note: You may find that search field predicate bindings filter results inconsistently with wildcard characters. This is due to a bug in NSArrayController
. The workaround is to create a subclass of NSArrayController
and override arrangeObjects:
to simply invoke super
‘s implementation.
© 2004, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-03-04)