young
(rtSwift)
1
Sorry for the confusing title. I hope my sample code is clear:
import Foundation
import SwiftUI
// A recursive struct
struct Item: Identifiable {
var name: String
var children: [Item]? // with a KeyPath to this optional field, I need a binding to this when this is not nil
let id = UUID()
}
// this code is simplified for asking this question
func recursiveView<Data>(@Binding data: Data, children: KeyPath<Data.Element, Data?>, rowContent: (Binding<Data.Element>) -> ()) where Data: MutableCollection, Data: RandomAccessCollection, Data.Index: Hashable, Data.Element: Identifiable {
let index = data.indices.first!
// from keyPath to Children, assume we already know the children property is not nil
// need the binding, now the value!!!
let bindingToChildren = $data[index][keyPath: children]! // 👈👈👈 how to get the binding the children property (Binding<[Data.Element]>)?
// 👆 the above doesn't compile
// in my real code: error: Cannot convert value of type 'Any' to expected argument type 'Binding<Data>'
// here: error: Key path of type 'KeyPath<Data.Element, Data?>' cannot be applied to a base of type 'Binding<Data.Element>'
}
struct Stub {
@State var data = [
Item(name: "Curly", children:[
Item(name: "Dorothy")
])
]
func body() {
recursiveView($data: $data, children: \.children, rowContent: { $item in })
}
}
jackmarch
(Jack March)
2
children: KeyPath<Data.Element, Data?>
This is the core of your issue, the reason you're not getting a binding is because you're looking up member of type Data?, not Binding<[Data.Element]>
But the second issue is you're expecting a different type of the children property than it actually is:
how to get the binding the children property (Binding<[Data.Element]>)?
The type of the property children is not [Data.Element], it's just Data.
This compiles:
struct Stub {
@State var data = [
Item(name: "Curly", children:[
Item(name: "Dorothy")
])
]
func body() {
recursiveView(data: $data, children: \.children)
}
func recursiveView<Data>(
data: Binding<Data>,
children: KeyPath<Binding<Data.Element>, Binding<Data?>>
) where
Data: MutableCollection,
Data: RandomAccessCollection,
Data.Index: Hashable,
Data.Element: Identifiable
{
let index = data.wrappedValue.indices.first
let foo = data[index!]
let bindingToChildren: Binding<Data?> = foo[keyPath: children]
}
}
young
(rtSwift)
3
Note: recursiveView() uses SE-0293 property wrapper function parameter.
I need to call recursiveView(...) recursively. I change the signature and now can't get pass this compile error:
import Foundation
import SwiftUI
// A recursive struct
struct Item: Identifiable {
var name: String
var children: [Item]?
let id = UUID()
}
// this code is simplified for asking this question
// vvvvv <=== change this to match `bindingToChildren` below
func recursiveView<Data>(@Binding data: Data?, children: KeyPath<Binding<Data.Element>, Binding<Data?>>, rowContent: (Binding<Data.Element>) -> ()) where Data: MutableCollection, Data: RandomAccessCollection, Data.Index: Hashable, Data.Element: Identifiable {
let index = data!.indices.first!
// this is Binding<Data?>, is there anyway to get Binding<Data> so as to don't have to change the first param of `recursiveView(...)`?
let bindingToChildren = $data[index][keyPath: children] // 👈👈👈 error: Value of optional type 'Data?' must be unwrapped to refer to member 'subscript' of wrapped base type 'Data'
// ^? 👈👈👈 unwrap here doesn't work
recursiveView(data: bindingToChildren, children: children, rowContent: rowContent)
}
struct Stub {
@State var data: [Item]? = [
Item(name: "Curly", children:[
Item(name: "Dorothy")
])
]
func body() {
recursiveView($data: $data, children: \.children, rowContent: { $item in })
}
}