6

tl;dr: type something here that won't crash if birthDate is nil:

xcdatamodel screenshot


I have an entity with a birthDate attribute and a fetched property with the following predicate, typed straight into the xcdatamodel file:

$FETCH_SOURCE.birthDate > birthDate

This happily returns a list of managed objects older than FETCH_SOURCE (the managed object where the fetch request is happening). But birthDate is optional, and if the birthDate for FETCH_SOURCE is nil...

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'can't use NULL on left hand side'

Note that it's fine if the attribute is nil for the object being compared. If I swap the arguments of the predicate:

birthDate < $FETCH_SOURCE.birthDate

...I get the same error. Trying to test it for nil:

(birthDate != nil AND birthDate < $FETCH_SOURCE.birthDate)

...gives...

'NSInvalidArgumentException', reason: 'illegal comparison with NULL'

Google hasn't heard of either of those errors so I'm thinking this is a recent state of affairs. The problem remains when it's a String being compared for equivalence, or whatever. Is there a way to fix this in the predicate so it correctly returns an empty list? Thanks.

Edit: To be clear, the question is whether it's possible to fix this crash in the predicate in the xcdatamodel file.

Update: The crash specifically happens when the Relationship fault for the NSFetchedPropertyDescription is triggered by trying to read anything about it, presumably because it doesn't attempt to run the predicate till then. It isn't nil however, and can be checked for nil without crashing.

DenverCoder9
  • 3,635
  • 3
  • 31
  • 57
  • possible duplicate of [Can I use an NSPredicate in Swift with a nil argument?](http://stackoverflow.com/questions/24684193/can-i-use-an-nspredicate-in-swift-with-a-nil-argument) – Icaro Jun 22 '15 at 22:48
  • Icaro, they have similar title keywords but are completely different questions. Are you a bot? ;) – DenverCoder9 Jun 22 '15 at 22:55
  • No just trying to help, but wouldn't that be cool! ;) Anyway my understand from the last question was you can compare with nil but you cannot use a nil value to compare with something, and that seems to be what my testing show, but I will let the explanation to someone else as I don't really know why! – Icaro Jun 22 '15 at 23:08
  • I see what you mean, but it looks like that question is about passing nil in a variable argument list, which just happened to be being done while formatting a predicate, rather than having the nil in the actual predicate. On the contrary, it supports my suspicions that my problem is a new one, as Core Data was quite happy with a predicate of "parentFolder == nil" in Jul 10 '14. – DenverCoder9 Jun 22 '15 at 23:23
  • Have you tried using birthDate != [NSNull null] – Lneuner Jun 23 '15 at 09:57
  • @Lneuner, that fails to compile, it's not valid syntax for the predicate. "...xcdatamodeld: Compilation failed for data model at path..." – DenverCoder9 Jun 23 '15 at 10:44

2 Answers2

6

I can't find any way to amend the predicate of the fetched property to avoid this error, but one workaround is to implement a custom getter for the olderHeroes fetched property. This should return an empty array if the birthDate is nil.

var olderHeroes: NSArray {
    var array : NSArray
    if self.birthDate == nil {
        array = NSArray()
    } else {
        self.willAccessValueForKey("olderHeroes")
        array = self.primitiveValueForKey("olderHeroes") as! NSArray
        self.didAccessValueForKey("olderHeroes")
    }
    return array
}

Or in Objective-C:

-(NSArray *)olderHeroes {
    NSArray *array;
    if (self.birthDate == nil) {
        array = [NSArray array];
    } else {
        [self willAccessValueForKey:@"olderHeroes"];
        array = [self primitiveValueForKey:@"olderHeroes"];
        [self didAccessValueForKey:@"olderHeroes"];
    }
    return array;
}

(These snippets go in the implementation file for the subclass of the FETCH_SOURCE entity).

pbasdf
  • 21,386
  • 4
  • 43
  • 75
  • This works, and although not a fix in the xcdatamodel, it is a fix on the predicate, which satisfies my good-structure OCD. Bounty is yours unless someone can better it by bounty day... – DenverCoder9 Jun 30 '15 at 10:21
2

If you want to have results containing either a nil value or it has to be greater or smaller than a certain date use the following:

((birthDate == nil) OR (birthDate < %@))

This works for me. I hope this is what you're asking. Had a bit of trouble understanding your question.

If you're asking to check a variable you're passing in the predicate, might the following work (untested):

(($FETCH_SOURCE.birthDate == nil && birthDate == nil) OR ($FETCH_SOURCE.birthDate < birthDate))
Orion
  • 1,258
  • 2
  • 14
  • 32
  • This crashes in the same way as the OP when birthDate is nil. – DenverCoder9 Jun 25 '15 at 13:40
  • Added a tl;dr and picture that hopefully makes the question easier to understand. :) – DenverCoder9 Jun 25 '15 at 14:02
  • 1
    Yeah sorry I haven't worked with the predicate in the way that it's defined in the .xcdatamodeld file. I'd hoped it was an easy fix regarding the query. I had used a date check too where it was allowed to be nil and where the date had to be in the future. I'll check the whole $FETCH_SOURCE thing out real quick. – Orion Jun 25 '15 at 14:23
  • No, if the birthDate of the managed object where the fetch request is happening is nil, it crashes with the same error. It even crashes if that's all you write in the predicate. – DenverCoder9 Jun 25 '15 at 15:04