Search code examples
swiftnspredicate

Swift combining predicates LogicalType AND OR


How can i combine a mix AND OR condition in Swift predicate. I have a following query

 Select * from tblTemp where dept == 1 && (subdept == 11 || subdept == 12)

I can write two predicate with same operator but don't know how to combine them

    let deptPredicate = NSPredicate(format: "dept == %@", 1)
    let subdeptPredicate1 = NSPredicate(format: "subdept = %@", 11)
    let subdeptPredicate2 = NSPredicate(format: "subdept = %@", 12)
    let andPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.and, subpredicates: [deptPredicate, subdeptPredicate1])

Solution

  • NSCompoundPredicate is a subclass of NSPredicate, which means that the result of NSCompoundPredicate(type:subpredicates:) can be used in another compound predicate.

    Note however that the %@ format placeholder expects an NSObject instance:

    let deptPredicate = NSPredicate(format: "dept == %@", 1 as NSNumber)
    let subdeptPredicate1 = NSPredicate(format: "subdept = %@", 11 as NSNumber)
    let subdeptPredicate2 = NSPredicate(format: "subdept = %@", 12 as NSNumber)
    
    let orPredicate = NSCompoundPredicate(type: .or,
                                          subpredicates: [subdeptPredicate1, subdeptPredicate2])
    
    let andPredicate = NSCompoundPredicate(type: .and,
                                           subpredicates: [deptPredicate, orPredicate])
    

    Alternatively, use the %ld format for integers:

    let deptPredicate = NSPredicate(format: "dept == %ld", 1)
    // ... etc.
    

    There are also convenience initializers:

    let orPredicate = NSCompoundPredicate(orPredicateWithSubpredicates:
        [subdeptPredicate1, subdeptPredicate2])
    
    let andPredicate = NSCompoundPredicate(andPredicateWithSubpredicates:
        [deptPredicate, orPredicate])
    

    Compound predicates are very useful to combine a dynamic set of conditions at runtime. On the other hand, if only the values change then you can simply use "AND" and "OR" within the predicate format string:

    NSPredicate(format: "dept == %ld AND (subdept = %ld OR subdept = %ld)", 1, 11, 12)
    

    Finally note that you can use the #keyPath directive with the %K placeholder, so that the compiler fills in the correct property name (thus reducing the chance of typographical errors):

    let deptPredicate = NSPredicate(format: "%K == %ld", #keyPath(MyEntity.dept), 1)