Search code examples
iosswiftnspredicate

How to combine multiple nullable NSPredicates?


For eg, Something like:

    var finalPredicate = NSPredicate(format: "")

    if (screen != nil) {
        screenPredicate = NSPredicate(format: "screen = %@", screen!)
        finalPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [screenPredicate!])
    }

    if (feature != nil) {
        featurePredicate = NSPredicate(format: "feature = %@", feature!)
        finalPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [finalPredicate, featurePredicate!])
    }

    if (shouldDisplayPredicate != nil) {
        shouldDisplayPredicate = NSPredicate(format: "shouldDisplay = %@", shouldDisplay!)
        finalPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [finalPredicate, shouldDisplayPredicate!])
    }
    if (hasDisplayed != nil) {
        displayPredicate = NSPredicate(format: "hasDisplayed = %@", hasDisplayed!)
        finalPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [finalPredicate, displayPredicate!])
    }

Is there a better way to do this where the predicates could be null or not ?

Thanks


Solution

  • First, you should avoid the forced unwrapping and replace

    if screen != nil {
        // ... add predicate for `screen!` ...
    }
    

    by an optional binding:

    if let screenValue = screen {
        // ... add predicate for `screenValue` ...
    }
    

    Compare When should I compare an optional value to nil? for a good overview of that topic. The same can be achieved more compactly using the map() method of Optional:

    screen.map { /* ... add predicate for `$0` ... }
    

    The closure is called only if screen != nil, and then $0 inside the closure is the unwrapped value.

    Second, it is simpler to fill an array with all required predicates first, and create the compound predicate only once. This also allows you to check if any search attribute was set at all.

    Your code then becomes

    var predicates: [NSPredicate] = []
    if let screenValue = screen {
        predicates.append(NSPredicate(format: "screen = %@", screenValue))
    }
    if let featureValue = feature {
        predicates.append(NSPredicate(format: "feature = %@", featureValue))
    }
    // ... other search attributes ...
    if !predicates.isEmpty {
        let finalPredicate = NSCompoundPredicate(andPredicateWithSubpredicates:predicates)
    }
    

    or

    var predicates: [NSPredicate] = []
    screen.map { predicates.append(NSPredicate(format: "screen = %@", $0)) }
    feature.map { predicates.append(NSPredicate(format: "feature = %@", $0)) }
    // ... other search attributes ...
    if !predicates.isEmpty {
        let finalPredicate = NSCompoundPredicate(andPredicateWithSubpredicates:predicates)
    }