I didn't find any posts/ways to load/manage different objects in the same view in SwiftUI. The goal is to not multiply same code.
Supposing I have 3 structs/instances Country, Region, Subregion with same properties: id, name, image.
struct Country: Identifiable, Codable, Hashable {
var id: Int64?
var name: String
var image: String
}
struct Region: Identifiable, Codable, Hashable {
var id: Int64?
var name: String
var image: String
}
struct Subregion: Identifiable, Codable, Hashable {
var id: Int64?
var name: String
var image: String
}
In a a parent view, I want to send instances to the child view as a binding:
ButtonNavigationLink(descriptorItem: descriptorItem, selectedItem: $finderViewModel.referenceCountry)
ButtonNavigationLink(descriptorItem: descriptorItem, selectedItem: $finderViewModel.referenceRegion)
ButtonNavigationLink(descriptorItem: descriptorItem, selectedItem: $finderViewModel.referenceSubregion)
Note: I don't want to send the same properties to the child view, this is easy. I want to send the struct itself (model). I need it in the child view.
So now, I want to receive the model/struct in the child view. I have done multiple try such:
@Binding var selectedItem: GenericType?
=> the problem is I don't know how to read the properties from the generic (cast ?)@Binding var selectedItem: AnyHashable?
=> the compiler is lost between the type sent (country) and AnyHashable typeWell, I am lost. :-)
My subview that receive the "generic" binding looks like that:
// struct ButtonNavigationLink <T>: View where T: Hashable { // => if generic
struct ButtonNavigationLink: View {
var descriptorItem: DescriptorItem
@Binding var selectedItem: AnyHashable?
// Generic type
//@Binding var selectedItem: GenericType?
//typealias GenericType = T
}
So what's the best solution to not multiply views for each object ? The child view goal is to select the item in the @Binding, not only to display data... that's why I need to full object and not only the properties as @Binding.
Button (action: {
selectedItem = descriptorItem
})
Thanks in advance for any help/suggestion.
The most obvious way to do this is to define a protocol:
protocol Item {
var id: Int64? { get }
var name: String { get }
var image: String { get }
}
Then make your structs conform to that protocol:
struct Country: Identifiable, Codable, Hashable, Item {
struct Region: Identifiable, Codable, Hashable, Item {
struct Subregion: Identifiable, Codable, Hashable, Item {
And finally, change your binding to use the protocol type:
struct DetailsView: View {
@Binding var selectedItem: Item?
var body: some View {
VStack {
if let selectedItem {
if selectedItem is Region { // <- If you want to check which type was passed
Text("Region name: \(selectedItem.name)")
}
}
}
}
}
struct ContentView: View {
@State var selectedItem: Item?
var body: some View {
VStack {
Button("Set to Region") {
selectedItem = Region(id: 1, name: "region", image: "Image")
}
Button("Set to Country") {
selectedItem = Country(id: 0, name: "contry", image: "Image")
}
NavigationStack {
NavigationLink("Pass \(selectedItem?.name ?? "nil")", destination: DetailsView(selectedItem: $selectedItem))
}
}
}
}