Using NSPredicateEditor with DADataTable

Today I want to talk about NSPredicateEditor and how to use it with a DADataTable.

What is NSPredicateEditor…

Cocoa’s NSPredicateEditor class provides an excellent way to compose predicates at runtime. The main benefit is that the user needs to know nothing about predicates and their format, but with help of NSPredicateEditor can easily build quite complex conditions they need. Using NSPredicateEditor also allows to avoid any kind of typing errors, because in most of cases all the user needs is to do is just to select appropriate fields and values from popup buttons.

Here is the screenshot of Bugs 7 showing NSPredicateEditor that represents following condition: (Live == 1 AND AreaID == 4 AND OwnerID == 5) OR ID == 37222

![]( "Bugs-NSPredicate")
*NSPredicateEditor* uses a list of *NSPredicateEditorRowTemplate* objects, where each row template describes certain elementary comparison predicate object.  Combining different row templates allows the *NSPredicateEditor* to represent compound predicates with unlimited complexity.

Usually, a row template holds three elements: left and right expressions and a condition between them. In our case, the left expression represents the column from our table and the right expression represents the value pattern for that column. Obviously, it is good to have template with most suitable editor for each column data type. DataAbstract for OS X provides proper templates for each fields that can be used in the table. In addition to standard  NSPredicateEditorRowTemplate DA brings several custom row templates that simplify building predicates for the tables.

The most interesting here is the DALookupFieldPredicateEditorRowTemplate class. This class allows to create row template for a lookup field, where the user can chose certain lookup value from list of allowed values, proposed by a popup button. It allows user to deal with meaningful values instead of just IDs. This is well shown in the figure above.

I should also mention DAFallbackPredicateEditorRowTemplate. This class describes special template for any predicate that is not supported by a given set of templates. For example if your editor tries to render a NSPredicate that comes into your application from outside and cannot understand a certain part of the compound predicate, then it will render it as “unsupported predicate” with help of DAFallbackPredicateEditorRowTemplate. This assures that the sub-predicate, while not editable in the NSPredicateEditor, will not get lost.

…and How Can You Use it?

Let’s see how to configure NSPredicateEditor instance for composing predicates for a certain custom DADataTable.

When you need to configure the editor for exposing a sub-set of columns only, you need to create array of row templates obtained from each table field you want to see in editor:

NSMutableArray*rowTemplates =[NSMutableArray arrayWithCapacity:[[da bugs] fieldCount]]; for(NSString*f in allowedFields){ DAFieldDefinition *fd =[[da bugs] fieldByName:f]; [rowTemplates addObjectsFromArray:[fd predicateEditorRowTemplates]]; }
Then you should supplement that array with additional row template for building compound predicates
NSPredicateEditorRowTemplate*compoundTemplate =[[[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes:[NSArray arrayWithObjects:[NSNumber numberWithInt: NSAndPredicateType], [NSNumber numberWithInt: NSOrPredicateType], [NSNumber numberWithInt: NSNotPredicateType], nil]] autorelease]; [rowTemplates addObject:compoundTemplate];
and (optionally) add an instance of *DAFallbackPredicateEditorRowTemplate* for rendering unsupported predicates.
[rowTemplates addObject:[DAFallbackPredicateEditorRowTemplate rowTemplate]];
Finally, you should assign that templates array to your *NSPredicateEditor *instance
[editor setRowTemplates:rowTemplates];
If you want to expose **all** visible fields in the editor, then you can just use the convenient *defaultPredicateEditorRowTemplates]* method on the table, and whole configuration will be reduced to single line of code, as followings:
[editor setRowTemplates:[[da bugs] defaultPredicateEditorRowTemplates]];
That’s all. Your predicate editor is properly configured and you can use it for building any kind of predicates for given table.

You can use that editor to let the user define NSPredicates both for local filtering data in table like:

NSPredicate*p =[editor predicate]; rows =[[[myTable rows] filteredArrayUsingPredicate:p] retain]; [tableView reloadData];
or for creating **Dynamic Where** for server-side filtering data.
NSPredicate*p =[editor predicate]; DADynamicWhereClause *clause =[DADynamicWhereClause dynamicWhereClauseWithPredicate:p]; request =[rda beginGetDataTable:@"MyTable" select:[self fieldsToSelect] where:clause start:NO]; [request setDelegate:self]; [request start];
I recomend to review our **DAFilters **sample that ships with DA/OSX, to see *NSPredicateEditor* and the techniques shown here in action.