[SOLVED] SwiftUI and Swift 5.7: Issue with View protocol and Opaque Types

Issue

This Content is from Stack Overflow. Question asked by Nahuel Roldan

I have a compilation error when i try to return different custom views that conform to the View protocol from a method. Here is a simple example that describes the issue.

I want to have a method that returns a different custom view based on the enum case. When i try to achieve that, i get different compile errors:

    enum AppScreen: String, CaseIterable {
        case home
        case detail
    }
    
    struct ContentView: View {
        var body: some View {
            NavigationView {
                List {
                    ForEach(AppScreen.allCases, id: .self) { screen in
                        NavigationLink(destination: getSomeView(screen)) {
                            Text(screen.rawValue)
                        }
                    }
                }
                List {
                    ForEach(AppScreen.allCases, id: .self) { screen in
                        NavigationLink(destination: getAnyView(screen)) {
                            Text(screen.rawValue)
                        }
                    }
                }
            }
        }
    
        private func getSomeView(_ screen: AppScreen) -> some View {
            switch screen {
            case .home:
                return HomeView()
            case .detail:
                return DetailView()
            }
        }
        
        private func getAnyView(_ screen: AppScreen) -> any View {
            switch screen {
            case .home:
                return HomeView()
            case .detail:
                return DetailView()
            }
        }
    }```

The method *getSomeView* throws the following compile error: *Function declares an opaque return type 'some View', but the return statements in its body do not have matching underlying types*

The method *getAnyView* compiles, but i get the following error when i call it as the destination for the NavigationLink: *Type 'any View' cannot conform to 'View'*

I'm learning SwiftUI and the new generics features from Swift 5.7. I believe that the behavior that i'm looking for can be achieved. Any help or guidance will be appreciated, thanks in advance!



Solution

This is how to achieve what you are trying to do.

  1. Mark getSomeView() with @ViewBuilder. This makes it work like var body which is also a ViewBuilder allowing you to build different types of views.
  2. Remove the return statements.

Here is a standalone example based upon your original code:

enum AppScreen: String, CaseIterable {
    case home
    case detail
}

struct HomeView: View {
    var body: some View {
        Text("HomeView")
    }
}

struct DetailView: View {
    var body: some View {
        Text("DetailView")
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                ForEach(AppScreen.allCases, id: \.self) { screen in
                    NavigationLink(destination: getSomeView(screen)) {
                        Text(screen.rawValue)
                    }
                }
            }
        }
    }

    @ViewBuilder
    private func getSomeView(_ screen: AppScreen) -> some View {
        switch screen {
        case .home:
            HomeView()
        case .detail:
            DetailView()
        }
    }
}


This Question was asked in StackOverflow by Nahuel Roldan and Answered by vacawama It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

people found this article helpful. What about you?