I have defined a CustomProtocol, which requires a unique identifier. I have also created a CustomModel that implements this protocol. Despite having an id property as required by the Identifiable protocol, I am unable to use CustomModel as an identifiable type in SwiftUI.
protocol CustomProtocol: Identifiable {
var id: String { get }
}
struct CustomModel: CustomProtocol {
let id = UUID().uuidString
}
class CustomModelStore: ObservableObject {
@Published var models: [any CustomProtocol] = []
init() {
models = Array(repeating: CustomModel(), count: 10)
}
}
struct CustomProtocolView: View {
@StateObject var store = CustomModelStore()
@State var selectedModel: (any CustomProtocol)?
var body: some View {
VStack {
ForEach(store.models) { model in
Text(model.id)
.font(.footnote)
.onTapGesture {
selectedModel = model
}
}
.sheet(item: $selectedModel) { model in
Text(model.id)
.font(.subheadline)
}
}
}
}
struct CustomProtocolView_Previews: PreviewProvider {
static var previews: some View {
CustomProtocolView()
}
}
Of course I can specify id in ForEach, but this way doesn't acceptable for me because I have no chance do it in .sheet or .fullScreenCover view modifiers. In my store I also can't change type from CustomProtocol to CustomModel
This is the standard "protocol existentials (
anytypes) do not conform to protocols."selectedModeldoes not conform to CustomProtocol (or to Identifiable). Only concrete types conform to protocols.As Baglan notes, you can deal with the ForEach by passing
id: \.id, but you'll have to redesign the.sheetto not require Identifiable, or you'll need to get rid of theany CustomProtocoland use a concrete type.Exactly how that works depends a lot on exactly what CustomProtocol does and what kinds of objects are in
models. If they're all the same object (like in your example), then you would generally make CustomModelStore and CustomProtocolView generic:If you have different types in the array, then it will depend on how all of this really works to design the right solution. Generally the answer is to
.mapthe objects to some concrete Model struct that the view uses rather than the underlying objects, but it depends on your problem.