I'm getting the following error:
Value of protocol type 'MenuProtocol' cannot conform to 'Identifiable'; only struct/enum/class types can conform to protocols
I have a main menu which some of its options have a submenu. I decided to use an enum
for the main menu, and another enum
for any submenus. All of the enums implement a protocol I defined, which allows me to specify the text that I'm going to show for each menu option.
I'm not sure if this is the best approach, but I found it useful.
Any ideas how can I fix this error? Thanks for your help!
Protocol MenuProtocol {
var submenu: [OptionProtocol] { get }
}
protocol OptionProtocol {
var name: String { get }
}
enum MainMenu: Int, Identifiable, CaseIterable, MenuProtocol, OptionProtocol {
case One
case Two
case Three
case Four
case Five
var id: Int { rawValue }
var name: String {
switch self {
case .One:
return "One"
case .Two:
return "Two"
case .Three:
return "Three"
case .Four:
return "Four"
case .Five:
return "Five"
}
}
var submenu: [OptionProtocol] {
switch self {
case .One:
return SubMenu1.allCases
case .Two:
return SubMenu2.allCases
default:
return []
}
}
}
enum SubMenu1: Int, Identifiable, CaseIterable, OptionProtocol {
case SubMenu1_Option1
case SubMenu1_Option2
case SubMenu1_Option3
case SubMenu1_Option4
case SubMenu1_Option5
var id: Int { rawValue }
var name: String {
switch self {
case .SubMenu1_Option1:
return "Submenu1 Option 1"
case .SubMenu1_Option2:
return "Submenu1 Option 2"
case .SubMenu1_Option3:
return "Submenu1 Option 3"
case .SubMenu1_Option4:
return "Submenu1 Option 4"
case .SubMenu1_Option5:
return "Submenu1 Option 5"
}
}
}
enum SubMenu2: Int, Identifiable, CaseIterable, OptionProtocol {
case SubMenu2_OptionA
case SubMenu2_OptionB
case SubMenu2_OptionC
case SubMenu2_OptionD
case SubMenu2_OptionE
var id: Int { rawValue }
var name: String {
switch self {
case .SubMenu2_OptionA:
return "Submenu2 Option A"
case .SubMenu2_OptionB:
return "Submenu2 Option B"
case .SubMenu2_OptionC:
return "Submenu2 Option C"
case .SubMenu2_OptionD:
return "Submenu2 Option D"
case .SubMenu2_OptionE:
return "Submenu2 Option E"
}
}
}
struct EnumProtTest: View {
var body: some View {
VStack {
HStack {
ForEach(MainMenu.allCases) { value in
Text("\(theName(value))")
.padding()
.background(Color.blue)
}
}
HStack {
TheContentView(data: MainMenu.One.submenu) { item in
Text("\(item.name)")
.padding()
.background(Color.purple)
}
}
HStack {
TheContentView(data: MainMenu.Two.submenu) { item in
Text("\(item.name)")
.padding()
.background(Color.purple)
}
}
}
}
}
struct TheContentView<Data: RandomAccessCollection, ElementView: View>: View where Data.Element: Identifiable, Data.Element: Hashable {
var data: Data
var itemView: (Data.Element) -> ElementView
var body: some View {
ForEach(data) { item in
itemView(item)
.padding()
.background(Color.purple)
}
}
}
ForEach
expects an Identifiable
type, that's why your Menu*-types conform to Identifiable
. But what is passed to ForEach
is a MenuProtocol
that doesn't conform to Identifiable
.
In general, when upcasting an object (eg MainMenu
) to a base-class or protocol (in your case MenuProtocol
), the compiler is only able to access the properties/functions that are provided by that protocol. This limitation is due the fact that you may also pass other objects that conform to the protocol but miss all the other properties like the id
In your case.
In your example I don't see a reason for having a protocol-oriented implementation for the Menu, because you don't use MainMenu
and SubMenu1
in a polymorphism way. My suggestion when it comes to protocols and generics and inheritance: try to keep it as simple as possible without any of those features and add those if you cannot solve your problem without.
Good WWDC video and podcast about protocol-oriented programming https://developer.apple.com/wwdc15/408 https://www.swiftbysundell.com/podcast/71/
You can use indices
and retrieve your data using an index. Just change your HStack
.
HStack {
ForEach(MainMenu.One.items().indices) { index in
let value = MainMenu.One.items()[index]
Text(value.name())
.padding()
.background(Color.purple)
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.