Grant Emerson

Powerful Predicates

Powerful Predicates — SwiftMoji Entry #26

The Foundation framework exposes a powerful class called NSPredicate for filtering data. An NSPredicate is initialized using a formatting String written in Apple’s predicate syntax. The initializer also takes in a variadic argument list of values that can be dynamically inserted into the specified format String. In the code example, a Craft object is created with three properties: name, maxAltitude, and maxSpeed. The class definition is preceded with the @objcMembers attributed which allows the Swift properties to interact with the underlying Objective-C code in the NSPredicate API. The Array of Crafts must be cast to an NSArray to use the filtered(using:) method. The defined predicate filters out any elements of the crafts NSArray which don’t have a name beginning with “Gulfstream” and maxAltitude between 25,000 and 50,000 ft. The “%@” is used to substitute an object (in this case a String) from the argument list into the predicate. Additionally, the “%K” can be used to substitute in different key paths. The “[c]” tied to the end of the BEGINSWITH declaration allows the String comparison to be executed without case sensitivity. The predicate syntax is fairly straightforward. It should be easy to adopt with or without a background in querying languages such as SQL. Although Apple seems to be promoting value-based and functional programming alternatives to some of these older OOP APIs, they are still valuable to learn because many frameworks such as Core Data still rely heavily on them.

import Foundation

@objcMembers class Craft: NSObject {
    let name: String
    let maxAltitude: Int
    let maxSpeed: Int
    
    init(name: String, maxAltitude: Int, maxSpeed: Int) {
        self.name = name
        self.maxAltitude = maxAltitude
        self.maxSpeed = maxSpeed
    }
    
    override var description: String { name }
}

let 🛩 = Craft(name: "Gulfstream G550", maxAltitude: 49500, maxSpeed: 585)
let 🛬 = Craft(name: "gulfstream g650", maxAltitude: 51000, maxSpeed: 610)
let 🚀 = Craft(name: "BFR", maxAltitude: Int.max, maxSpeed: 18000)
let 🚁 = Craft(name: "Bell 407", maxAltitude: 17618, maxSpeed: 162)
let crafts = [🛩, 🛬, 🚀, 🚁] as NSArray

let predicate = NSPredicate(format: "(name BEGINSWITH[c] %@) AND (maxAltitude BETWEEN { 25000, 50000 })", "Gulfstream")
let listings = crafts.filtered(using: predicate)
print(listings) // [Gulfstream G550]
Tagged with: