Introduction to the

NSPredicate is a class in the base library used to filter the acquired data. It is similar to THE WHERE statement in SQL, but it provides a more natural and high-level language to define logical conditional statements on the query of the data set.

A direct demonstration of the use of NSPredicate may be easier to understand than an abstract explanation, but before we do that, let’s look at the basic syntax.

Basic grammar

  • Parser Basics (Predicate Strings)

    Predicate format strings are sensitive to keywords and parentheses, ambiguous to Spaces, and do not perform semantic type detection.

    $: Declares variables starting with a $sign, such as $VARIABLE_NAME

    %K: attribute name

    %@ : Attribute value

    Using %@ placeholders can only represent expressions, not predicates, which would cause program exceptions.

  • Basic Word Order (Basic Comparison Operations)

    =, == : Checks whether the expression on the left equals the expression on the right.

    >=, => : Checks whether the expression on the left is greater than or equal to the expression on the right.

    <=, =< : Checks whether the expression on the left is less than or equal to the expression on the right.

    > : Determines whether the left expression is greater than the right expression.

    < : Determines whether the left expression is less than the right expression.

    ! =, <> : Checks whether the expression on the left is different from the expression on the right.

    $INPUT BETWEEN {$LOWER, $UPPER}

  • Boolean Value Predicates (Boolen Predicates)

    TRUEPREDICATE: Predicate that always returns true.

    FALSEPREDICATE: PREDICates that always return false.

  • Basic Compound Predicates

    AND, && : logical AND.

    OR, | | : logical OR.

    NOT,! : Logical non.

  • String Word Order

    Strings are case-sensitive and sound sensitive by default, but we can turn them off manually. For example, firstName BEGINSWITH[CD] $FIRST_NAME. C, D Turn off sensitivity to letter case and pronunciation respectively.

    BEGINSWITH: determines whether the content of the left string BEGINSWITH the right string.

    CONTAINS: Determines whether the content of the string CONTAINS the string on the right.

    ENDSWITH: checks whether the content of the string ENDSWITH the string on the right.

    LIKE: checks whether the string on the left equals the string on the right. ? And * are usually used as wildcards here.

    ? : Matches a character.

    * : 0 or more characters are matched.

    Note: With wildcard? The combination of or * must be represented as a string.

    MATCHES: Checks whether the left and right expressions are equal using regular expressions.

    Utri-conforms TO and Utri-Equals: These two are part of the macOS development and are not discussed here.

  • Aggregate Operations

    ANY, SOME: specifies SOME elements that satisfy the following expressions. For example, ANY children. Age < 18

    ALL: specifies ALL elements that satisfy the following expression. For example, ALL children.age < 18

    NONE: Specifies elements that do not satisfy the following expression. For example, NONE children. Age < 18. Equivalent to NOT (ANY…) .

    IN: is equivalent to the SQL IN operation. Determines whether the set of elements on the left appears in the set of elements on the right. For example name IN {‘Ben’, ‘Melissa’, ‘Nick’}. These collections can be array, set, or dictionary. If it is dictionary, its values are taken.

    Array [index] : indicates the element represented by the specified index in array.

    Array [FIRST] : Specifies the FIRST element of the array.

    Array [LAST] : Specifies the LAST element of the array.

    Array [SIZE] : specifies the SIZE of the array.

  • Identifiers

    All C-style identifiers are not retained.

    #symbol: Escapes reserved words to user identifiers.

    [\]{octalDigit}{3} : Escape an 8-digit number. (followed by three octal digits)

    [\][xX]{hexdigit}{2} : Escape hexadecimal digits. (\x/\ x followed by 2 hexadecimal digits)

    [\][uU]{hexDigit}{4} : Escapes Unicode. (\u/\ u followed by four hexadecimal digits)

  • Literals

    Single quotation marks have the same effect as double quotation marks, but they do not end each other. For example, “ABC” is equivalent to ‘ABC’, but “a ‘b’ C “is equivalent to a + space + ‘b’ + space + c.

    FALSE, NO: logical FALSE.

    TRUE, YES: indicates the logical truth.

    NULL, NIL: indicates a NULL value.

    SELF: indicates the object being operated on.

    “Text” : indicates a string.

    ‘text’ : indicates a string.

  • Reserved Words

    AND, OR, IN, NOT, ALL, ANY, SOME, NONE, LIKE, CASEINSENSITIVE, CI, MATCHES, CONTAINS, BEGINSWITH, ENDSWITH, BETWEEN, NULL, NIL, SELF, TRUE, YES, FALSE, NO, FIRST, LAST, SIZE, ANYKEY, SUBQUERY, FETCH, CAST, TRUEPREDICATE, FALSEPREDICATE, UTI-CONFORMS-TO, UTI-EQUALS

Basic usage

Creating Predicate

  1. Creating a Predicate Using a Format String

    Normally we use the class method of NSPredicate + (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat,… ; Because the compiler does not detect strings for semantic typing, errors are not detected at compile time, so we have to be strict about writing it otherwise it will cause runtime errors in some cases when we replace variables.

  • String Constants, Variables, and Wildcards (String Constants/Variables, Wildcards)

    To insert a string constant into a predicate format string, use a pair of single or double quotation marks and the escape character \, For example, NSPredicate *predicate = [NSPredicate predicateWithFormat:@”lastName like[c] \”S*\””];

    Insert the placeholder %@ in the predicate format string, and the %@ representation is automatically quoted. It is important to note that %@ cannot be used with the wildcard * or? Direct use, and must use the splicing method. For example,

    NSString *prefix = @"prefix";
    NSString *suffix = @"suffix";
    NSPredicate *predicate = [NSPredicate
    predicateWithFormat:@"SELF like[c] %@",
    [[prefix stringByAppendingString:@"*"] stringByAppendingString:suffix]];
    BOOL ok = [predicate evaluateWithObject:@"prefixxxxxxsuffix"];
    Copy the code

    Inserts variables into the predicate format string, for example

    predicate = [NSPredicate
    predicateWithFormat:@"lastName like[c] $LAST_NAME"];
    Copy the code
  • Boolean Values (Boolean)

    Specifies and tests Boolean values for equality. For example,

    NSPredicate *newPredicate =
    [NSPredicate predicateWithFormat:@"anAttribute == %@", [NSNumber numberWithBool:aBool]];
    NSPredicate *testForTrue =
    [NSPredicate predicateWithFormat:@"anAttribute == YES"];
    Copy the code
  • Dynamic Property Names

    We cannot use %@ for attribute names because the value with the %@ placeholder will be quoted as a string variable. For example,

    NSString *attributeName = @"firstName";
    NSString *attributeValue = @"Adam";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ like %@",
    attributeName, attributeValue];
    Copy the code

    The predicate format string is “firstName” LIKE “Adam”.

    If you want to specify a dynamic property name, use %K. For example,

    predicate = [NSPredicate predicateWithFormat:@"%K like %@",
        attributeName, attributeValue];
    Copy the code

    The predicate format string becomes what we want firstName LIKE “Adam”.

  1. Creating Predicates Directly in Code

    Here is an example of code representing (Revenue >= 1000000) and (Revenue < 100000000).

    NSExpression *lhs = [NSExpression expressionForKeyPath:@"revenue"];
    
    NSExpression *greaterThanRhs = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:1000000]];
    NSPredicate *greaterThanPredicate = [NSComparisonPredicate
                                         predicateWithLeftExpression:lhs
                                         rightExpression:greaterThanRhs
                                         modifier:NSDirectPredicateModifier
                                         type:NSGreaterThanOrEqualToPredicateOperatorType
                                         options:0];
    
    NSExpression *lessThanRhs = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:100000000]];
    NSPredicate *lessThanPredicate = [NSComparisonPredicate
                                      predicateWithLeftExpression:lhs
                                      rightExpression:lessThanRhs
                                      modifier:NSDirectPredicateModifier
                                      type:NSLessThanPredicateOperatorType
                                      options:0];
    
    NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
                                      @[greaterThanPredicate, lessThanPredicate]];
    Copy the code
  2. Creating Predicates Using Predicate Templates

    This is a compromise between the programmatically error-prone nature of defining predicates by hard coding and the tedious nature of defining predicates by code. For example,

    NSPredicate *predicateTemplate = [NSPredicate
    predicateWithFormat:@"lastName like[c] \$LAST_NAME"];
    Copy the code

    Define the predicate template above as

    NSExpression *lhs = [NSExpression expressionForKeyPath:@"lastName"];
    
    NSExpression *rhs = [NSExpression expressionForVariable:@"LAST_NAME"];
    
    NSPredicate *predicateTemplate = [NSComparisonPredicate
                                      predicateWithLeftExpression:lhs
                                      rightExpression:rhs
                                      modifier:NSDirectPredicateModifier
                                      type:NSLikePredicateOperatorType
                                      options:NSCaseInsensitivePredicateOption];
    Copy the code

    We can use it like this

    NSPredicate *predicate = [predicateTemplate predicateWithSubstitutionVariables:
    [NSDictionary dictionaryWithObject:@"Turner" forKey:@"LAST_NAME"]].Copy the code

    So now this new predicate will become lastName LIKE[c] “Turner”

    The precondition for using dictionary substitution is that the dictionary must contain the key-value pairs of the variables specified by the predicate, so when we want to match a null value, we must supply a null value in the dictionary, for example

    NSPredicate *predicate = [NSPredicate
    predicateWithFormat:@"date = $DATE"];
    predicate = [predicate predicateWithSubstitutionVariables:
    [NSDictionary dictionaryWithObject:[NSNull null] forKey:@"DATE"]].Copy the code

    The predicate becomes date ==

    .

    Defining a predicate directly from the code is essentially a way for the system to help us turn the basic syntax we learned into enumerations that we can choose to create, avoiding hard-coding errors. For example the options

    Typedef NS_OPTIONS (NSUInteger NSComparisonPredicateOptions) {NSCaseInsensitivePredicateOption, / / not case sensitive, [c] NSDiacriticInsensitivePredicateOption namely, / / the pronunciation is not sensitive, namely [d] NSNormalizedPredicateOption, / / which [cd], and the system will optimize the performance of this option. };Copy the code

    When we need to combine several predicates, it’s more convenient to use the NSCompoundPredicate subclass of NSPredicate.

  3. Format String Summary

    The quoted %@, %K, and $VARIABLE are interpreted as strings, thus preventing any substitution.

    • @”attributeName == %@” : This predicate checks whether the value of the attributeNamed attribute is equal to the value referred to by %@, which can be NSDate, NSNumber, NSString, etc.

    • @”%K == %@” : This predicate checks whether the value of the key %K is equal to the value of %@.

    • @”name IN $NAME_LIST” : This predicate template checks if the key name is present IN the variable $NAME_LIST.

    • @” ‘name’ IN $NAME_LIST” : This predicate template checks if the string constant ‘name’ is present IN the variable $NAME_LIST.

    • @”$name IN $NAME_LIST” : This predicate template checks if the variable $name is present IN the variable $NAME_LIST.

    • @”%K == ‘%@’ : This predicate checks whether the value of %K is equal to the value of the string %@.

Using Predicate

  1. Evaluating the Predicates

    Here’s a simple example

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@"@ [@"Stig"The @"Shaffiq"The @"Chris"]];
    BOOL result = [predicate evaluateWithObject:@"Shaffiq"];
    Copy the code

    Note: Only classes that support KVC can use predicates.

  2. Using Predicates with Arrays

    Both arrays and mutable arrays support operations to filter array elements. But there is a difference.

    • Use NSArray:filteredArrayUsingPredicate:Method returns the filtered element through a new array.
    • Use NSMutableArray:filterUsingPredicate:Method operates on a primitive array, and only elements that meet the predicate’s requirements are preserved.

    For example,

    NSMutableArray *names = [@[@"Nick"The @"Ben"The @"Adam"The @"Melissa"] mutableCopy];
    
    NSPredicate *bPredicate = [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
    NSArray *beginWithB = [names filteredArrayUsingPredicate:bPredicate];
    // beginWithB contains { @"Ben" }.
    
    NSPredicate *ePredicate = [NSPredicate predicateWithFormat:@"SELF contains[c] 'e'"];
    [names filterUsingPredicate:ePredicate];
    // names now contains { @"Ben"The @"Melissa" }
    Copy the code
  3. Using Predicates with key-paths

    For example,

    NSString *departmentName = ... ;
    NSPredicate *predicate = [NSPredicate predicateWithFormat: @"department.name like %@", departmentName];
    Copy the code

    If you have a one-to-many relationship, the predicate structure is a little different. If we want to get the apartments of all employees whose first name is “Matthew”, we can use ANY:

    NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"ANY employees.firstName like 'Matthew'"];
    Copy the code

    If we want to know what departments employees who earn more than a certain value are in:

    float salary = ... ;
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY employees.salary > %f", salary];
    Copy the code

Application scenarios

  • The test code
// MARK: - Stringlet str = "hello holy! it's so cold today!"
        let p01 = NSPredicate(format: "SELF CONTAINS 'cold'")
        if p01.evaluate(with: str) {
            print("p01: ")}let p02 = NSPredicate(format: "SELF LIKE[c] 'hello'")
        if p02.evaluate(with: str) {
            print("p02: ")}let p03 = NSPredicate(format: "SELF LIKE[c] '*ello'")
        if p03.evaluate(with: str) {
            print("p03: ")}let p04 = NSPredicate(format: "SELF LIKE[c] '? ello'")
        if p04.evaluate(with: str) {
            print("p04: ")}let p05 = NSPredicate(format: "SELF LIKE '? Ello*'")
        if p05.evaluate(with: str) {
            print("p05: ")}let p06 = NSPredicate(format: "SELF LIKE[c] 'hello*! '")
        if p06.evaluate(with: str) {
            print("p06: ")}let p07 = NSPredicate(format: "SELF IN %@", str)
        if p07.evaluate(with: "hello") {
            print("p07: "} // MARK: - setlet alice = Person(firstName: "Alice", lastName: "Smith", age: 24, departmentName: "A")
        let bob = Person(firstName: "Bob", lastName: "Jones", age: 13, departmentName: "B")
        let charlie = Person(firstName: "Charlie", lastName: "Smith", age: 20, departmentName: "A")
        let quentin = Person(firstName: "Quentin", lastName: "Alberts", age: 20, departmentName: "C")
        let jack = Person(firstName: "Jack", lastName: "J", age: 18, departmentName: "C")
        letpeople: NSMutableArray = [alice, bob, charlie, quentin, jack] self.people = people // 1. Find someone whose lastName is Smithlet p1 = NSPredicate(format: "lastName = 'Smith'")
        let arr1 = people.filtered(using: p1)
        print("arr1: \(arr1)"); // 2. Find the person whose firstName is a variablelet p2 = NSPredicate(format: "firstName = %@"."Bob")
        let arr2 = people.filtered(using: p2)
        print("arr2: \(arr2)"Age >= 18let p3 = NSPredicate(format: "age >= 18")
        let arr3 = people.filtered(using: p3)
        print("arr3: \(arr3)"// 4. Use the countable array 'filter' method to modify the original array //let p4 = NSPredicate(format: "age = 18")
//        people.filter(using: p4)
//        print("people: \(people)") // 5. Find the person who lives in Apartment Alet p5 = NSPredicate(format: "department.name = 'A'")
        let arr5 = people.filtered(using: p5)
        print("arr5: \(arr5)") // 6. Is there anyone older than 25let p6 = NSPredicate(format: "ANY people.age > 25 ")
        if p6.evaluate(with: self) {
            print(P6: "")}else {
            print(P6: "no." ")} // 7. Persons aged 20 or abovelet p7 = NSPredicate { (evaluatedObject, _) -> Bool in
            return (evaluatedObject as! Person).age >= 20
        }
        let arr7 = people.filtered(using: p7)
        print("arr7: \(arr7)") / / 8."%K == %@"
        let p8 = NSPredicate(format: "%K == %@"."lastName"."Smith")
        let arr8 = people.filtered(using: p8)
        print("arr8: \(arr8)") / / 9.let p9t = NSPredicate(format: "lastName = $NAME")
        let p9 = p9t.withSubstitutionVariables(["NAME": "Smith"])
        let arr9 = people.filtered(using: p9)
        print("arr9: \(arr9)"(10. Between 18 and 20 years oldlet lhs = NSExpression(forKeyPath: "age")
        let greaterThanRhs = NSExpression(forConstantValue: 18)
        let greaterP = NSComparisonPredicate(leftExpression: lhs, rightExpression: greaterThanRhs, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.greaterThan, options: NSComparisonPredicate.Options.normalized)

        let lessThanRhs = NSExpression(forConstantValue: 20)
        let lessP = NSComparisonPredicate(leftExpression: lhs, rightExpression: lessThanRhs, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.lessThan, options: NSComparisonPredicate.Options.normalized)

        let p10 = NSCompoundPredicate(andPredicateWithSubpredicates: [greaterP, lessP])
        let arr10  = people.filtered(using: p10)
        print("arr10: \(arr10)") // MARK: - Verifylet testPhone = "13422222222"
        let phoneRegex = "^ ([0-9] (13) | (15 [^ 4 \ \ D]) | (18 [0, 0-9])) \ \ D {8} $"
        let p21 = NSPredicate(format: "SELF MATCHES %@", phoneRegex)
        if p21.evaluate(with: testPhone) {
            print("It's a cell phone!")}let testEmail = "[email protected]"
        let emailRegex = "[A - Z0-9 a-z. + - _ %] + @ [A - Za - Z0-9. -] + \ \ [A Za - z] {2, 4}"
        let p22 = NSPredicate(format: "SELF MATCHES %@", emailRegex)
        if p22.evaluate(with: testEmail) {
            print("Mailbox number!"Class Person: NSObject {@objc var firstName: String =""
        @objc var lastName: String = ""
        @objc var age: Int = 0
        @objc var department: Department

        convenience init(firstName: String, lastName: String, age: Int, departmentName: String) {
            self.init()
            self.firstName = firstName
            self.lastName = lastName
            self.age = age
            self.department.name = departmentName
        }

        override init() {
            department = Department()
            super.init()
        }

        override var description: String {
            return firstName + "" + lastName
        }
    }

    class Department: NSObject {
        @objc var name: String
        init(name: String = "") {
            self.name = name
        }
    }
Copy the code
  • Core Data

    NSFetchRequest has predicate properties, which are used to filter and retrieve data.

  • Verify the format

    It is mainly used in combination with regular expressions.

    1. Mailbox number regular expression:

    [A - Z0-9 a-z. + - _ %] + @ [A - Za - Z0-9. -] + \ \ [A Za - z] {2, 4}

    1. Regular expression for mobile phone number:

    ^ ([0-9] (13) | (15 [^ 4 \ \ D]) | (18 [0, 0-9])) \ \ D {8} $