Reputation: 51
I am facing an issue when trying to initialize a view in SwiftUI. Let me explain:
I have a view called ExternalView, this view has a constraint with a protocol called ToggleableProtocol, then, this view internally consumes a view called InternalView, which is a view that has a constraint to a Hashable protocol.
The error occurs when I try to create an instance of ExternalView and pass it an array of different structs that conform to the ToggleableItem protocol. The error says Type 'any TogleableItem' cannot conform to 'TogleableItem'
TogleableItem
public protocol TogleableItem: Hashable {
var text: String { get }
var isSelected: Bool { get set }
}
Structs conforming to the TogleableItem
struct FirstObject: TogleableItem {
var text = "FirstItem"
var isSelected = false
}
struct SecondObject: TogleableItem {
var text = "SecondItem"
var isSelected = false
}
ContentView
struct ContentView: View {
@State private var items: [any TogleableItem] = [
FirstObject(),
SecondObject()
]
var body: some View {
ExternalView(
items: items
) { isSelected, index, _ in
items[index].isSelected = isSelected
}
}
}
ExternalView
public struct ExternalView<T: TogleableItem>: View {
private let items: [T]
private let onItemTap: (Bool, Int, T) -> Void
public init(
items: [T],
onItemTap: @escaping (Bool, Int, T) -> Void
) {
self.items = items
self.onItemTap = onItemTap
}
public var body: some View {
InternalView(
items: items
) { index, element in
Text(element.text)
.foregroundColor(element.isSelected ? .red : .blue)
}
}
}
InternalView
struct InternalView<Element: Hashable, Content: View>: View {
private let items: [Element]
private let content: (Int, Element) -> Content
init(
items: [Element],
content: @escaping (Int, Element) -> Content
) {
self.items = items
self.content = content
}
var body: some View {
LazyHStack(spacing: 0) {
ForEach(items.indices, id: \.self) { index in
content(index, items[index])
}
}
}
}
Thanks!!
I have tried changing the items parameter, inside the ExternalView to something like [any TogleableItem] but in the end it causes a similar error when consuming the InternalView
Upvotes: 5
Views: 4277
Reputation: 51945
The problem with your code is that you define for instance ExternalView
as generic
ExternalView<T: TogleableItem>
then you say that T
can be of a (read one) type that conforms to TogleableItem
but you want to use the view for a mix of types that conforms to TogleableItem
.
The solution as I see it is to not make the view types generics and instead use TogleableItem
directly in the declarations (I have skipped a lot of code below for brevity)
public struct ExternalView: View {
private let items: [any TogleableItem]
private let onItemTap: (Bool, Int, any TogleableItem) -> Void
...
}
struct InternalView<Content: View>: View {
private let items: [any TogleableItem]
private let content: (Int, any TogleableItem) -> Content
...
}
Another way to solve it if InternalView should be able to use other types than those conforming to TogleableItem
is to use the original solution but without Element
conforming to Hashable
struct InternalView<Element, Content: View>: View {
private let items: [Element]
private let content: (Int, Element) -> Content
Upvotes: 3