Попробовал это на Xcode Beta 2
Я думаю, что это не ошибка, а скорее "особенность" системы типов Swift и SwiftUI API.
Если вы посмотрите на подпись ForEach
(просто Cmd + Нажмите на ForEach
)
public init(_ data: Data, content: @escaping (Data.Element.IdentifiedValue) -> Content)
вы можете заметить, что он принимает Data.Element.IdentifiedValue
тип
Итак, из вашего примера
struct ContentView : View {
var body: some View {
VStack {
ForEach(arrayOfFoos) { aFoo in
Text("\(aFoo.name) = \(aFoo.value)") // error aFoo is a Bar not a Foo
}
}
}
}
aFoo
локальное значение имеет тип Foo.IdentifiedValue
Давайте спросим Свифта, что он думает об этом типе:
Foo.IdentifiedValue.self == Bar.IdentifiedValue.self // true
Foo.IdentifiedValue.self == Foo.self // false
Foo.IdentifiedValue.self == Bar.self // true
Как видите, Foo.IdentifiedValue
на самом деле Bar
.
Чтобы обойти это, мы можем создать оболочку, используя новую функцию Swift 5.1 - 'Key Path Member Lookup'! : D
Я обновил ваш пример. Добавлен класс AnyBindable
и сопоставленные элементы arrayOfFoos
.
class Bar: BindableObject {
let didChange = PassthroughSubject<Void, Never>()
let name: String
init(name aName: String) {
name = aName
}
}
class Foo: Bar {
let value: Int
init(name aName: String, value aValue: Int) {
value = aValue
super.init(name:aName)
}
}
@dynamicMemberLookup
class AnyBindable<T: BindableObject>: BindableObject {
let didChange: T.PublisherType
let wrapped: T
init(wrapped: T) {
self.wrapped = wrapped
self.didChange = wrapped.didChange
}
subscript<U>(dynamicMember keyPath: KeyPath<T, U>) -> U {
return wrapped[keyPath: keyPath]
}
}
let arrayOfFoos = [ Foo(name:"Alpha",value:12), Foo(name:"Beta",value:13)]
.map(AnyBindable.init)
struct ContentView : View {
var body: some View {
VStack {
ForEach(arrayOfFoos) { aFoo in
Text("\(aFoo.name) = \(aFoo.value)") // it compiles now
}
}
}
}