I'm trying to save an NSPredicate
to the user defaults and I thought I should encode is like so, since this class conforms to NSSecureCoding
:
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"name == 'test'"];
NSData *filterPredicateData = [NSKeyedArchiver archivedDataWithRootObject:filterPredicate
requiringSecureCoding:YES
error:nil];
I then decode it.
NSPredicate *decodedPredicate = [NSKeyedUnarchiver unarchivedObjectOfClass:NSPredicate.class fromData:filterPredicateData error:nil];
if([filterPredicate isEqualTo:decodedPredicate]) { // YES
someNSArrayController.filterPredicate = filterPredicate; // this works
someNSArrayController.filterPredicate = decodedPredicate; // this throws an exception
[someNSArray filteredArrayUsingPredicate: decodedPredicate]; // this throws an exception
}
I'm getting this exception:
This predicate has evaluation disabled
Notes:
decodedPredicate
works as the predicate of an NSFetchRequest
without issue.
decodedPredicate
and filterPredicate
return the same predicateFormat
.
Can anyone explain this error?
You need to do [decodedPredicate allowEvaluation];
first before using it.
From the doc of allowEvaluation
:
Forces a securely decoded predicate to allow evaluation.
Discussion
When securely decodingNSPredicate
objects that are encoded usingNSSecureCoding
, evaluation is disabled because it is potentially unsafe to evaluate predicates you get out of an archive.
Before you enable evaluation, you should validate key paths, selectors, and other details to ensure no erroneous or malicious code will be executed. Once you’ve verified the predicate, you can enable the receiver for evaluation by callingallowEvaluation
.
Sample tests (if someone wants to verify it):
//Test Sample array
NSArray *array = @[@{@"name":@"test"}, @{@"name": @"other"}];
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"name == 'test'"];
NSArray *filtered1 = [array filteredArrayUsingPredicate:filterPredicate];
NSLog(@"Filtered: %@", filtered1);
NSError *encodingError = nil;
NSData *filterPredicateData = [NSKeyedArchiver archivedDataWithRootObject:filterPredicate
requiringSecureCoding:YES
error:&encodingError];
if (encodingError) {
NSLog(@"Encoding error: %@", encodingError);
}
NSError *decodingError = nil;
NSPredicate *decodedPredicate = [NSKeyedUnarchiver unarchivedObjectOfClass:NSPredicate.class
fromData:filterPredicateData
error:&decodingError];
if (decodingError) {
NSLog(@"Decoding error: %@", decodingError);
}
NSLog(@"Initial: %@", filterPredicate); //Let's check the `description` if there it's seems correct
NSLog(@"Decoded: %@", decodedPredicate); //Let's check the `description` if there it's seems correct
if ([filterPredicate isEqualTo:decodedPredicate]) { // YES
[decodedPredicate allowEvaluation]; //Comment this line and it will fail
NSArray *filtered2 = [array filteredArrayUsingPredicate:decodedPredicate];
NSLog(@"Filtered with decoded predicate: %@", filtered2);
}