Home > database >  SwiftUI and Swift 5.7: Issue with View protocol and Opaque Types
SwiftUI and Swift 5.7: Issue with View protocol and Opaque Types

Time:09-18

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!

CodePudding user response:

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()
        }
    }
}
  • Related