In a ViewModel I have:
public var models: [any Tabbable]
And Tabbable starts with
public protocol Tabbable: Identifiable {
associatedtype Id
var id: Id { get }
/// ...
}
In Swift in my ViewModel I can use the Array.forEach to:
models.forEach { _ in
print("Test")
}
But in SwiftUI I can't use ForEach to:
ForEach(viewModel.models) { _ in
Text("Test")
}
Due to:
Type 'any Tabbable' cannot conform to 'Identifiable'
Any suggestions? This is with Swift 5.7. Do I need to go back to making something like AnyTabbable? Thanks in advance.
CodePudding user response:
Consider this example in the form of a Playground:
import UIKit
import SwiftUI
public protocol Tabbable: Identifiable {
associatedtype Id
var id: Id { get }
var name : String { get }
}
struct TabbableA : Tabbable{
typealias Id = Int
var id : Id = 3
var name = "TabbableA"
}
struct TabbableB : Tabbable {
typealias Id = UUID
var id : Id = UUID()
var name = "TabbableB"
}
struct ViewModel {
public var models: [any Tabbable]
}
let tabbableA = TabbableA()
let tabbableB = TabbableB()
let models: [any Tabbable] = [tabbableA, tabbableB]
struct ContentView {
@State var viewModel : ViewModel = ViewModel(models: models)
var body : some View {
ForEach(viewModel.models) { model in
Text(model.name)
}
}
}
In this case, we have the type TabbableA
where each instance has an id
property that is an integer (for the sake of the sample they only use "3" but it's the type that is significant). In TabbableB
each instance's id
is a UUID. Then I create an array where one item is an instance of TabableA
and another is an instance of TabbableB
. The array is of type [any Tabbable]
.
Then I try to use ForEach
on the array. But some elements in the array use Int
ids and some use UUID
ids. The system doesn't know what type to use to uniquely identify views. Ints
and UUIDs
can't be directly compared to one another to determine equality. While each item that went into the array is Tabbable
, and therefore conforms to Identifiable
, the elements coming out of the array, each of which is of type any Tabbable
, do not conform to Identifiable
. So the system rejects my code.
CodePudding user response:
You can provide a KeyPath to the id (or any unique variable): parameter which specifies how to retrieve the ID
in ForEach
. It is because all items in ForEach
must be unique. You can create a structure, which conforms to your protocol. At that moment it should work. The second option is to remove Identifiable
from the protocol and then you can use that directly.
public protocol Tabbable {
var id: String { get }
/// ...
}
public var models: [Tabbable]
ForEach(viewModel.models, id: \.id) { _ in
Text("Test")
}
or
public protocol TabbableType: Identifiable {
associatedtype Id
var id: Id { get }
/// ...
}
struct Tabbable: TabbableType {
var id: String { get }
/// ...
}
public var models: [Tabbable]
ForEach(viewModel.models) { _ in
Text("Test")
}