Created
May 19, 2020 09:52
-
-
Save iljaiwas/e7faab710a86be50d3a5f8af8461e8de to your computer and use it in GitHub Desktop.
Tinkering with a predicate builder in Objective-C.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* I'm trying to come up with with a general predicate builder in Objective-C, that can build NSPredicates for | |
arbitrary classes. I want to write something like this: | |
predicate = [[[[[builder name] contains:@"ilja"] age] greaterThan:@20] predicate]; | |
But the compiler insists on sprinkling ids everywhere. | |
predicate = [[(id)[[(id) [(id) builder name] contains:@"ilja"] age] greaterThan:@20] predicate]; | |
Any ideas how to omit the 'id' casts? Also, need a good idea for or and not methods. | |
Feel free to paste this code into CodeRunner. | |
*/ | |
#import <Foundation/Foundation.h> | |
@interface Person: NSObject | |
@property NSString *name; | |
@property NSNumber *age; /* I' rather have an int property, but the compiler doesn't like sending messages to int */ | |
@end | |
@implementation Person | |
@end | |
@interface IHPredicateBuilder: NSObject | |
+ (id) predicateBuilderForClass:(Class) inClass; | |
- (id) contains:(NSString*) value; | |
- (id) equals:(id) value; | |
- (id) lessThan:(NSNumber*) value; | |
- (id) lessThanOrEqual:(NSNumber*) value; | |
- (id) greaterThan:(NSNumber*) value; | |
- (id) greaterThanOrEqual:(NSNumber*) value; | |
/* No idea how to this so far | |
- (id) not:(IHPredicateBuilder*) predicate; | |
- (id) or:(IHPredicateBuilder*) inPredicates; | |
*/ | |
@property Class targetClass; | |
@property NSString *lastKey; | |
@property NSPredicateOperatorType lastOperatorType; | |
@property NSPredicate *predicate; | |
@end | |
@implementation IHPredicateBuilder | |
+ (id) predicateBuilderForClass:(Class) inClass | |
{ | |
IHPredicateBuilder *result = [[self alloc] init]; | |
result.targetClass = inClass; | |
return result; | |
} | |
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector | |
{ | |
NSString *selectorName = NSStringFromSelector( aSelector); | |
if ([self.targetClass instancesRespondToSelector:aSelector]) { | |
NSString *selectorName = NSStringFromSelector( aSelector); | |
self.lastKey = selectorName; | |
return [self methodSignatureForSelector:@selector(mirror)]; | |
} | |
return [super methodSignatureForSelector:aSelector]; | |
} | |
- (void)forwardInvocation:(NSInvocation *)anInvocation | |
{ | |
if ([self.targetClass instancesRespondToSelector:anInvocation.selector]) { | |
anInvocation.selector = @selector(mirror); | |
[anInvocation invokeWithTarget:self]; | |
return; | |
} | |
[super forwardInvocation:anInvocation]; | |
} | |
- (id) mirror | |
{ | |
return self; | |
} | |
- (id) contains:(NSString*) inValue | |
{ | |
[self addPredicateWithOperatorType: NSContainsPredicateOperatorType value:inValue]; | |
return self; | |
} | |
- (id) equals:(id) inValue | |
{ | |
[self addPredicateWithOperatorType: NSEqualToPredicateOperatorType value:inValue]; | |
return self; | |
} | |
- (id) lessThan:(NSNumber*) inValue | |
{ | |
[self addPredicateWithOperatorType: NSLessThanPredicateOperatorType value:inValue]; | |
return self; | |
} | |
- (id) lessThanOrEqual:(NSNumber*) inValue | |
{ | |
[self addPredicateWithOperatorType: NSLessThanOrEqualToPredicateOperatorType value:inValue]; | |
return self; | |
} | |
- (id) greaterThan:(NSNumber*) inValue | |
{ | |
[self addPredicateWithOperatorType: NSGreaterThanPredicateOperatorType value:inValue]; | |
return self; | |
} | |
- (id) greaterThanOrEqual:(NSNumber*) inValue | |
{ | |
[self addPredicateWithOperatorType: NSGreaterThanOrEqualToPredicateOperatorType value:inValue]; | |
return self; | |
} | |
- (void) addPredicateWithOperatorType:(NSPredicateOperatorType) inType value:(id) inValue | |
{ | |
NSPredicate *newPredicate = [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:self.lastKey] | |
rightExpression:[NSExpression expressionForConstantValue:inValue] | |
modifier:NSDirectPredicateModifier | |
type:inType | |
options:NSDiacriticInsensitivePredicateOption]; | |
if (self.predicate == nil) { | |
self.predicate = newPredicate; | |
} else { | |
self.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[self.predicate, newPredicate]];; | |
} | |
} | |
/* No idea yet | |
- (id) or:(NSArray<IHPredicateBuilder*>*) inPredicates | |
{ | |
} | |
- (id) not:(IHPredicateBuilder*) predicate | |
{ | |
} | |
*/ | |
@end | |
int main(int argc, char *argv[]) { | |
@autoreleasepool { | |
Person *dummy = [[Person alloc] init]; | |
dummy.name = @"ilja"; | |
dummy.age = @100; | |
IHPredicateBuilder *builder = [IHPredicateBuilder predicateBuilderForClass:Person.class]; | |
NSPredicate *predicate = [[(id)[[(id) [(id) builder name] contains:@"ilja"] age] greaterThan:@20] predicate]; | |
NSLog (@"should evaluate to 1: %d", [predicate evaluateWithObject:dummy]); | |
dummy.age = @5; | |
NSLog (@"should evaluate to 0: %d", [predicate evaluateWithObject:dummy]); | |
} | |
} |
[UINavigationBar appearance] returns an object conforming to protocol UIAppearance . I'm wondering why they can send messages such as setTintColor there without the compiler raising an error message. 🤷♂️
I bet the magic lies in UI_APPEARANCE_SELECTOR
So that's defined as
#define UI_APPEARANCE_SELECTOR attribute((annotate("ui_appearance_selector")))
Looks like I'd need to hack the compiler to get rid of the warnings?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Would
UIAppearance
be a good inspiration? Where you call, for example:[[UINavigationBar appearance] setTintColor:...]
? The biggest difference is that you want to "re-implement" the properties ofPerson
returning theIHPredicateBuilder
.