Some would probably argue that the way it is meant to be done is in Interface Builder. That would probably be fine if you're familiar with how row templates work, how they get combined, etc. Personally, I prefer to create the programmatically because I can be explicit about how the row templates function.
Let's go through an example of how this all works together, and then you can decide which approach works best for you:
- You have an object with two
NSString
properties:bar
andbaz
bar
can only have a certain number of possible values, which we'll call@"Bar1"
,@"Bar2"
, and@"Bar3"
.baz
can be anyNSString
value.
With this in mind, here's how you'd create that programmatically:
// these defines are for brevity on the web page; please don't actually do this
#define NSPERT NSPredicateEditorRowTemplate
#define KP(_kp) [NSExpression expressionForKeyPath:(_kp)]
#define CV(_cv) [NSExpression expressionForConstantValue:(_cv)]
NSPERT *compound = [[NSPERT alloc] initWithCompoundTypes:@[@(NSAndPredicateType),
@(NSOrPredicateType),
@(NSNotPredicateType)]];
NSPERT *bar = [[NSPERT alloc] initWithLeftExpressions:@[KP(@"bar")]
rightExpressions:@[CV(@"Bar1"), CV(@"Bar2"), CV(@"Bar3")]
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType),
@(NSNotEqualToPredicateOperatorType)]
options:0];
NSPERT *baz = [[NSPERT alloc] initWithLeftExpressions:@[KP(@"baz")]
rightExpressionAttributeType:NSStringAttributeType
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType),
@(NSNotEqualToPredicateOperatorType)]
options:0];
NSArray *templates = @[compound, bar, baz];
[self.myPredicateEditor setRowTemplates:templates];
Granted, this is a lot of typing just to create row templates. I understand that. I just think that it's also very explicit and easy to debug. That being said, you can also configure this exact same thing in Interface Builder. To do so, put an NSPredicateEditor
in your xib, and give it 3 row templates. The Compound row template you'd leave alone, but the other two you'd change to be configured like this:
For the "bar
" template:
For the "baz
" template:
If you compare these screenshots to the code above, it should (hopefully) be easy to see how one maps to the other. With the row templates configured like this, IB also shows the predicate editor in your window:
If you want to do more advanced things, such as creating custom row templates, or populating the list of allowed constant values with something from your data model, then you'll have to drop to doing the configuration in code.
Edit to answer comment question
The modifier
bit of these selectors corresponds to the NSComparisonPredicateModifer
for the created NSComparisonPredicate
. There are three modifiers: Direct, Any, and All.
@"bar = 'Bar1'";
^ No modifier means this is NSDirectPredicateModifier
@"ANY user.userName = 'markjs'";
^~~ use of "ANY" keyword means this is NSAnyPredicateModifier
@"ALL user.age < 42";
^~~ use of "ALL" keyword means this is NSAllPredicateModifier
In these three examples, they're all comparison predicates (because they have a left side, an operator, and a right side), but the use of "ANY" or "ALL" affects how the left side is interpreted. If either is present, then the predicate is expecting the left-hand side to evaluate to a collection of possibilities.
Using the "direct" option (or 0
) basically means you're going to be doing a one-to-one comparison.