SwiftUI Core Data:使用 NSPredicate 过滤 @FetchRequest

\color{red}{\Large \mathbf{Hacking \quad with \quad iOS: SwiftUI \quad Edition}}

{\Large \mathbf{Core \ Data}}

Filter @FetchRequest - 韦弦zhy

当我们使用SwiftUI的@FetchRequest属性包装器时,我们可以提供一个排序描述符数组来控制结果的顺序,但是我们也可以提供NSPredicate来控制应显示哪些结果。谓词是简单的测试,该测试将应用于我们的Core Data实体中的每个对象——只有通过测试的对象才会包含在结果数组中。

NSPredicate的语法不是您可以轻易猜到的,但实际上,您只会想要几种谓词,因此它并不像您想的那样糟糕。

要尝试一些谓词,请创建一个名为Ship的新实体,该实体具有两个字符串属性:“name”和“universe”。

现在将ContentView.swift修改为:

import CoreData
import SwiftUI

struct ContentView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Ship.entity(), sortDescriptors: [], predicate: nil) var ships: FetchedResults<Ship>

    var body: some View {
        VStack {
            List(ships, id: \.self) { ship in
                Text(ship.name ?? "Unknown name")
            }

            Button("Add Examples") {
                let ship1 = Ship(context: self.moc)
                ship1.name = "Enterprise"
                ship1.universe = "Star Trek"

                let ship2 = Ship(context: self.moc)
                ship2.name = "Defiant"
                ship2.universe = "Star Trek"

                let ship3 = Ship(context: self.moc)
                ship3.name = "Millennium Falcon"
                ship3.universe = "Star Wars"

                let ship4 = Ship(context: self.moc)
                ship4.name = "Executor"
                ship4.universe = "Star Wars"

                try? self.moc.save()
            }
        }
    }
}

现在,我们可以点击按钮,将一些样本数据注入Core Data,但是现在我们没有谓词。为了解决这个问题,我们需要用某种可以应用于我们的对象的测试谓词来代替nil

例如,我们可以要求来自 Star Wars 的船只,如下所示:

@FetchRequest(entity: Ship.entity(),
              sortDescriptors: [],
              predicate: NSPredicate(format: "universe == 'Star Wars'")
) var ships: FetchedResults<Ship>

如果您的数据包含引号,那将变得很复杂,因此更常见的是使用特殊的语法:“%@”表示“在此处插入一些数据”,并允许我们将数据作为谓词的参数而不是内联。

因此,我们可以这样写:

NSPredicate(format: "universe == %@", "Star Wars"))

除了==之外,我们还可以使用诸如<>之类的比较来过滤对象。例如,这将返回Defiant,Enterprise和Executor:

NSPredicate(format: "name < %@", "F")) var ships: FetchedResults<Ship>

%@在后台做了很多工作,尤其是在将本机Swift类型转换为等效的Core Data时。例如,我们可以使用IN谓词来检查 universe 是否是数组中三个选项之一,如下所示:

NSPredicate(format: "universe IN %@", ["Aliens", "Firefly", "Star Trek"])

我们还可以使用谓词来检查字符串的一部分,例如使用BEGINSWITHCONTAINS这样的运算符。例如,这将返回所有以大写字母E开头的船:

NSPredicate(format: "name BEGINSWITH %@", "E"))

该谓词区分大小写;如果要忽略大小写,则需要对此进行修改:

NSPredicate(format: "name BEGINSWITH[c] %@", "e"))

CONTAINS[c]的工作方式类似,除了可以从子字符串开始,它可以位于属性内的任何位置。

最后,您可以使用NOT翻转谓词,以获得其常规行为的反函数。例如,这将查找所有以E开头的船舶:

NSPredicate(format: "NOT name BEGINSWITH[c] %@", "e"))

如果您需要更复杂的谓词,请使用AND将它们连接起来以建立所需的尽可能多的精度,或者为Core Data添加导入并查看NSCompoundPredicate——它使您可以从多个较小的谓词中构建一个谓词。

译自 Filtering @FetchRequest using NSPredicate