How to declare a generic view

I'm trying to build a menu using the code below. I've run into some difficulty declaring the destination view in the struct.

I get the error: Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements

The designation of course will be the a view in the app.
Any help would be greatly appreciated.

Code Block
import SwiftUI
struct MenuList: Identifiable {
var id: Int
var image: String
var name: String
var destination: View // This line is where the problem is
}
extension MenuList {
static func getMenu() -> [MenuList] {
return[
MenuList(id: 1, image: "house", name: "Welcome", destination: Welcome_View()),
MenuList(id: 2, image: "text.justify", name: "MW List", destination: TrickList_View()),
MenuList(id: 3, image: "list.bullet", name: "My Collection", destination: MyCollection_View()),
MenuList(id: 4, image: "text.badge.plus", name: "Telly Sheet", destination: Tally_View()),
MenuList(id: 5, image: "rectangle.and.paperclip", name: "Want List", destination: WantList_View()),
MenuList(id: 6, image: "gear", name: "Settings", destination: Settings_View()),
MenuList(id: 7, image: "arrowshape.turn.up.right", name: "Share", destination: Share_View()),
MenuList(id: 8, image: "questionmark.circle.fill", name: "Help", destination: Help_View()),
MenuList(id: 9, image: "photo.on.rectangle", name: "Presentation", destination: Presentation_View())]
}
}

Answered by OOPer in 618232022
You can make your MenuList generic in this way.
Code Block
struct MenuList<TheView: View>: Identifiable {
    var id: Int
    var image: String
    var name: String
    var destination: TheView
}

(By the way, each MenuList represents an item in a menu, not a list...)

But this cannot be the solution for your issue. You cannot declare an Array of non-specialized generic type in Swift.
(And Xcode 11 shows weird messages when you use the generic MenuList above.
Code Block
Cannot convert value of type 'Welcome_View' to expected argument type 'TheView'

With the wrong Fix-it suggestion:
Code Block
Insert 'as! TheView'

Never tap the Fix button, which never fixes.)


Maybe using AnyView is the easiest solution for your issue.
Code Block
struct MenuItem: Identifiable {
    var id: Int
    var image: String
    var name: String
    var destination: AnyView
}
extension MenuItem {
    init<TheView: View>(id: Int, image: String, name: String, destination: TheView) {
        self.id = id
        self.image = image
        self.name = name
        self.destination = AnyView(destination)
    }
}
extension MenuItem {
    static func getMenu() -> [MenuItem] {
        return[
            MenuItem(id: 1, image: "house", name: "Welcome", destination: Welcome_View()),
            MenuItem(id: 2, image: "text.justify", name: "MW List", destination: TrickList_View()),
            MenuItem(id: 3, image: "list.bullet", name: "My Collection", destination: MyCollection_View()),
//...
]
    }
}

But having Views inside non-View data types may cause some unexpected behavior and you may need to fix that in the near future.
Accepted Answer
You can make your MenuList generic in this way.
Code Block
struct MenuList<TheView: View>: Identifiable {
    var id: Int
    var image: String
    var name: String
    var destination: TheView
}

(By the way, each MenuList represents an item in a menu, not a list...)

But this cannot be the solution for your issue. You cannot declare an Array of non-specialized generic type in Swift.
(And Xcode 11 shows weird messages when you use the generic MenuList above.
Code Block
Cannot convert value of type 'Welcome_View' to expected argument type 'TheView'

With the wrong Fix-it suggestion:
Code Block
Insert 'as! TheView'

Never tap the Fix button, which never fixes.)


Maybe using AnyView is the easiest solution for your issue.
Code Block
struct MenuItem: Identifiable {
    var id: Int
    var image: String
    var name: String
    var destination: AnyView
}
extension MenuItem {
    init<TheView: View>(id: Int, image: String, name: String, destination: TheView) {
        self.id = id
        self.image = image
        self.name = name
        self.destination = AnyView(destination)
    }
}
extension MenuItem {
    static func getMenu() -> [MenuItem] {
        return[
            MenuItem(id: 1, image: "house", name: "Welcome", destination: Welcome_View()),
            MenuItem(id: 2, image: "text.justify", name: "MW List", destination: TrickList_View()),
            MenuItem(id: 3, image: "list.bullet", name: "My Collection", destination: MyCollection_View()),
//...
]
    }
}

But having Views inside non-View data types may cause some unexpected behavior and you may need to fix that in the near future.
Thanks.
I did tried AnyView but the secondary extension was what was needed.
How to declare a generic view
 
 
Q