gpt4 book ai didi

swift - 为更复杂的协议(protocol)键入删除

转载 作者:搜寻专家 更新时间:2023-11-01 06:26:37 25 4
gpt4 key购买 nike

我正在构建一个声明式的基于类型的过滤器模型。我无法将事件过滤器的状态存储在属性中,因为我的协议(protocol)具有关联类型。``

我听说过Type Erasure,但我找到的所有示例都使用 super 简单的示例,但不知何故我无法将其映射到我的用例。

这是我的协议(protocol):

protocol Filter {
// The Type to be filtered (`MyModel`)
associatedtype ParentType
// The type of the property to be filtered (e.g `Date`)
associatedtype InputType
// The type of the possible FilterOption (e.g. `DateFilterOption` or the same as the Input type for filtering in enums.)
associatedtype OptionType
// This should return a list of all possible filter options
static var allOptions: [OptionType] { get }

static var allowsMultipleSelection: Bool { get }
// the adopting object will be setting this.
var selectedOptions: [OptionType] { get set }

func isIncluded(_ item: InputType) -> Bool
// For getting reference to the specific property. I think Swift 4's keypaths could be working here too.
var filter: FilterClosure<ParentType> { get }
}

以及具有减少复制/粘贴代码扩展的子协议(protocol)

protocol EquatableFilter: Filter where InputType: Equatable, OptionType == InputType {}
extension EquatableFilter {
var allowsMultipleSelection: Bool { return true }
func isIncluded(_ item: InputType) -> Bool {
if selectedOptions.count == 0 { return true }
return selectedOptions.contains(item)
}
}

// Another specific filter. See gist file for extension.
protocol DateFilter: Filter where InputType == Date, OptionType == DateFilterOption {}

更多代码,please see my gist通过示例模型查看我的实现方式。


问题

  1. 如何存储包含 struct 实例的数组,符合不同的 Filter 协议(protocol)?

  2. 我如何存储一个只包含结构类型的静态数组,以便我可以访问静态属性?

最佳答案

有趣的是,我今年早些时候为一个商业项目构建了一些与此不同的东西。笼统地去做是有挑战性的,但是大部分问题都来自于逆向思考。 “以终为始。”

// I want to be able to filter a sequence like this:
let newArray = myArray.filteredBy([
MyModel.Filters.DueDateFilter(selectedOptions: [.in24hours(past: false)]),
MyModel.Filters.StatusFilter(selectedOptions: [.a, .b])
])

这部分非常简单。它甚至不需要 filteredBy .只需添加 .filter每个元素:

let newArray = myArray
.filter(MyModel.Filters.DueDateFilter(selectedOptions: [.in24hours(past: false)]).filter)
.filter(MyModel.Filters.StatusFilter(selectedOptions: [.a, .b]).filter)

如果你愿意,你可以通过这种方式编写过滤,并做同样的事情:

func filteredBy(_ filters: [(Element) -> Bool]) -> [Element] {...}

重点是Filter这里并不是真正的“过滤器”。它是对过滤器的描述,还有很多关于 UI 的其他内容(我们稍后会详细讨论)。要实际过滤,您只需要 (Element) -> Bool .

我们真正想要的是一种构建 ([Element]) -> Element 的方法用一个漂亮的,富有表现力的语法。在函数式语言中,这会非常简单,因为我们有部分应用程序和函数组合之类的东西。但是 Swift 并不真的喜欢做这些事情,所以为了让它更漂亮,让我们构建一些结构。

struct Filter<Element> {
let isIncluded: (Element) -> Bool
}

struct Map<Input, Output> {
let transform: (Input) -> Output
}

我们需要一种开始的方式,所以让我们使用恒等映射

extension Map where Input == Output {
init(on: Input.Type) { transform = { $0 }}
}

我们需要一种方法来考虑 keyPaths

extension Map {
func keyPath<ChildOutput>(_ keyPath: KeyPath<Input, ChildOutput>) -> Map<Input, ChildOutput> {
return Map<Input, ChildOutput>(transform: { $0[keyPath: keyPath] })
}
}

最后我们要创建一个实际的过滤器

extension Map {
func inRange<RE: RangeExpression>(_ range: RE) -> Filter<Input> where RE.Bound == Output {
let transform = self.transform
return Filter(isIncluded: { range.contains(transform($0)) })
}
}

为“过去 24 小时”添加助手

extension Range where Bound == Date {
static var last24Hours: Range<Date> { return Date(timeIntervalSinceNow: -24*60*60)..<Date() }
}

现在我们可以构建一个如下所示的过滤器:

let filters = [Map(on: MyModel.self).keyPath(\.dueDate).inRange(Range.last24Hours)]

filters类型为 Filter<MyModel> , 所以任何其他过滤 MyModel 的东西在这里是合法的。调整你的 filteredBy :

extension Sequence {
func filteredBy(_ filters: [Filter<Element>]) -> [Element] {
return filter{ element in filters.allSatisfy{ $0.isIncluded(element) } }
}
}

好的,这就是过滤步骤。但是您的问题基本上也是“UI 配置”,为此您想要捕获比这更多的元素。

但是您的示例用法不会让您到达那里:

// Also I want to be able to save the state of all filters like this
var activeFilters: [AnyFilter] = [ // ???
MyModel.Filters.DueDateFilter(selectedOptions: [.in24hours(past: false)]),
MyModel.Filters.StatusFilter(selectedOptions: [.a, .b])
]

如何转换 AnyFilter进入 UI 元素?您的过滤器协议(protocol)实际上允许任何 选项类型。如果选项类型为 OutputStream,您将如何显示 UI或 DispatchQueue ?您创建的类型没有解决问题。

这是一种解决方法。创建一个 FilterComponent 结构,它定义所需的 UI 元素并提供一种构建过滤器的方法。

struct FilterComponent<Model> {
let optionTitles: [String]
let allowsMultipleSelection: Bool
var selectedOptions: IndexSet
let makeFilter: (IndexSet) -> Filter<Model>
}

然后要创建一个日期过滤器组件,我们需要一些日期选项。

enum DateOptions: String, CaseIterable {
case inPast24hours = "In the past 24 hours"
case inNext24hours = "In the next 24 hours"

var dateRange: Range<Date> {
switch self {
case .inPast24hours: return Date(timeIntervalSinceNow: -24*60*60)..<Date()
case .inNext24hours: return Date()..<Date(timeIntervalSinceNow: -24*60*60)
}
}
}

然后我们想要一种方法来创建具有正确 makeFilter 的组件:

extension FilterComponent {
static func byDate(ofField keyPath: KeyPath<Model, Date>) -> FilterComponent<Model> {
return FilterComponent(optionTitles: DateOptions.allCases.map{ $0.rawValue },
allowsMultipleSelection: false,
selectedOptions: [],
makeFilter: { indexSet in
guard let index = indexSet.first else {
return Filter<Model> { _ in true }
}
let range = DateOptions.allCases[index].dateRange
return Map(on: Model.self).keyPath(keyPath).inRange(range)
})
}
}

有了这些,我们可以创建类型为 FilterComponent<MyModel> 的组件.无需公开内部类型(如 Date)。无需协议(protocol)。

let components = [FilterComponent.byDate(ofField: \MyModel.dueDate)]

关于swift - 为更复杂的协议(protocol)键入删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53598104/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com