这个 Swift 包允许您基于您提供的 KeyPath
,在后台线程中对任何 Sendable
数组进行 搜索 和 排序,并且以 类型安全 的方式进行。
您应该在您的项目中使用 Swift Package Manager 导入此包。
要将此包导入到您现有的项目,只需打开 File -> Add Package Dependencies 并复制粘贴 URL 到 SPM。
要导入到 Swift 包,请将以下 URL 添加到您的包依赖项中
.package(url: "https://github.com/mohammad-nej/SearchAndSort", .upToNextMajor(from: "1.0.1")),
您还应该将其作为产品添加到您的 targets 中: .product(name: "SearchAndSort", package: "SearchAndSort"),
此包基于 KeyPaths
为 Sendable
类型提供了不同类型的搜索和排序功能。
假设我们有一个 Student
类型
struct Student : Sendable {
let name : String
let age : Int
let grade : Double
let birthDate : Date
}
如果我们想要基于 name
对 Student
数组进行排序
let students : [Studnet] = []
// we fill the students array
let nameKey = SortableKeyPath(\Student.name)
let sortedArray = nameKey.sort(students, order: .ascending)
为了创建 SortableKeyPath
,您的类型应该是 Sendable
,并且 key 应该遵循 Comparable
协议,否则您将收到编译时错误。
如果您希望能够基于多个 Key
进行排序,您可以使用 AnySortableKey
类型擦除,这将擦除您的 Key
类型
let nameKey = SortableKeyPath(\Student.name)
let ageKey = SortableKeyPath(\Student.age)
let keys : [AnySortableKey] = [.init(nameKey), .init(ageKey)]
注意: 这只会擦除 Key
的类型,因此您不能在同一个数组中存储不同模型的 SortableKeyPath
。
为了能够基于 KeyPath
进行搜索,您需要提供一个遵循 Stringifier
协议的类型。此协议只有一个方法
public protocol Stringifier : Sendable {
associatedtype Model
func stringify(_ model : Model) -> [String]
}
假设我们要为 Int
类型实现此协议
struct IntStringifier : Stringifier, Sendable {
func stringify(_ model: Int) -> [String] {
var results : [String] = []
results.append(model.formatted())
results.append(model.description)
return results
}
}
这样,当 BackgroundSearcher
迭代您的模型时,它会将每个 Int
转换为一个 字符串数组,并将其与您的搜索查询匹配。例如,使用此 IntStringifier
,如果用户输入 "2000" 或 "2,000" 作为查询,他们都将获得匹配。
好消息: 任何遵循 CustomStringConvertibale
协议的类型都将自动接收一个 Stringifier
,该 Stringifier
将返回其 description
。
话虽如此,让我们创建 SearchableKey
let nameKey = SearchableKeyPath(\Student.name)
let ageKey = SearchableKeyPath(\Student.age)
let dateKey = SearchableKeyPath( \Student.birthDate, stringifier: MyOwnStringifier())
我们现在可以使用这些 key 在任何 Student
类型的数组上进行搜索
let searchResult = await nameKey.search(in:studnets , for: "John")
let searchResult = await ageKey.search(in:studnets , for: "John", strategy: .exact)
注意: 如果在完成之前取消搜索函数,它将返回 nil
;如果找不到任何内容,它将返回一个空数组。 注意: 您可以在不同的匹配策略之间进行选择,默认值是 .contains
。
就像排序一样,您也可以使用 AnySearchableKey
来类型擦除您的 key 并将它们存储在一个数组中
let keys : [AnySearchableKey] = [.init(nameKey), .init(ageKey)]
这是一个 actor,它实际为您执行搜索。它将根据您的数组大小在内部创建 Task.detached
。
您可以将您的模型和 key 传递给此类型,并让它为您执行搜索。
let searcher = BackgroundSearcher(models: studnets,keys: [.init(nameKey),.init(ageKey)])
await searcher.search("John" ,withKey: [.init(nameKey)] , strategy: .prefix)
注意
withKey:
参数提供值,它将迭代您在初始化期间提供的所有 Key
。SearchableKeyPath
调用 search
时,会在内部创建一个 BackgroundSearcher
实例。1500
,BackgroundSearcher
将创建多个 Task
,您可以通过设置 minimumElementsToMultiThread
来更改此数字。就像 BackgroundSearcher
一样,Sorter
将允许您在一个地方存储所有模型和 key
let nameSort = SortableKeyPath(\Student.name)
let ageSort = SortableKeyPath(\Student.age)
let sorter = Sorter(models: students, keys: [.init(nameSort),.init(ageSort))
注意: 与 BackgroundSearcher
不同,无论您的数组大小如何,Sorter
始终会创建一个 Task
。
此类型可用于为您的 key 提供标题(例如,您想在 UI 中显示它)。
let titledNameKey = TitledKey(title: "Name", key: \Student.name)
此类型可以被 SearchableKeyPath
替换,如果您的提供的 Key
遵循 Comparable
协议,您也可以使用它来代替 SortableKeyPath
。
因此它可以作为 BackgroundSearcher
、AnySearchableKey
和 Sorter
、AnySortableKey
(如果 key 是 Comparable
)的输入。使用此类型的唯一缺点是,如果您只想进行排序,则始终必须为您的 Key
类型提供 Stringifier
。
此类型也是一种类型擦除,可以包含 SortableKey
、SearchableKey
、TitledKey
,甚至可以直接从 KeyPath
创建。
let sortableKey = SortableKeyPath(\Student.name, order: .ascending)
let birthDayKey = SearchableKeyPath(\Student.birthDate,stringifier: .persian)
let nameKey = TitledKey(title: "Name", key: \Student.name)
let anykey = AnyKey(birthDayKey,sortOrder: .ascending)
let anyKey2 = AnyKey(nameKey,sortOrder:.ascending)
let anyKey3 = AnyKey(sortableKey)
此包是完全 类型安全 的,您不会出错,并且会得到编译时错误。它也基于 Swift 6,因此它是完全并发安全的。
此包免费提供使用。如果您想了解更多信息,请随时在 GitHub 上或使用我的电子邮件联系我:mohammad.nej@gmail.com