Method to return a generic View type in SwiftUI

Hey, making the jump into SwiftUI and hit a hurdle. For my app home screen I want a struct to sift through various states and return the appropriate View based on a condition. In Swift I would usually apply some sort of Manager object to review the data states and return a class that inherits from UIView - but trying to do the same same in SwiftUI is causing me some problems - Example here:

Code Block
struct HomeScreenViewDirector {
     func fetchView() -> View {
        if someDataState {
            return PopulatedHomeScreen()
        } else {
            return EmptyView()
        }
    }
}
struct EmptyHomeScreen: View {
    var body: some View {
        HStack {
            navigationBarTitle("Empty State")
        }
    }
}
struct PopulatedHomeScreen: View {
    var body: some View {
         navigationBarTitle("Populated State")
    }
}

Attempting to do this generates the error
Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements

on the -> View return type. From some research it looks like I could change this to some View and they wrap all my returns as AnyView but that feels like it's the wrong approach.

Any tips on how and why to handle this

Cheers!

Accepted Reply

I found the answer elsewhere
Code Block struct HomeScreenViewDirector {
@ViewBuilder
func fetchView() -> some View {
if someDataState {
PopulatedHomeScreen()
} else {
EmptyView()
}
}
}

thanks for checking out !

Replies

feels like it's the wrong approach

I do not understand why. Any reasons?

My return type is View on my function - my structs conform to View - Adding the extra step of wrapping in AnyView() is an additional step for what feels like it will be a common problem and also carries performance costs (see hackingwithswift.com/quick-start/swiftui/how-to-return-different-view-types).


You need to consider your solution depending on the facts.
  • You cannot use View as a return type

  • PopulatedHomeScreen and EmptyView are different types, there's no common ancestor type

And I know there may be some possible performance issue on using AnyView.
But have you read any reports how much cost you need to pay?
In a report I read, it says using AnyView was faster than avoiding it. (Sorry, I cannot find the link now.)
The article you have shown does not contain any reliable info about why it can conclude that Group is more efficient.

Anyway, thanks for showing why you thought in that way.
Please do not hesitate to use Group when it can solve your issue.

I guess this is just my lack of understanding of Swift UI, but
  • "PopulatedHomeScreen and EmptyView are different types, there's no common ancestor type"

seems a strange given they both declare conformance to View

Although the approaches in the article would work, I would be interested to see if someone with more experience than myself has knowledge of a better approach as I assume this would be a common problem
  • It's easy to think it should work that way - mostly because it should. Since they're structs, there's no path of ancestry. However, as structs are told to act a certain way - ostensibly following an interface. " : (some) View" SHOULD mean "I can use this where ever I can use a View." It should work that way.

    It does not because you're not in a world where you can say something like "this thing IS A view", but rather "this thing ACTS LIKE a view". Small but important distinction. :\

Add a Comment
I found the answer elsewhere
Code Block struct HomeScreenViewDirector {
@ViewBuilder
func fetchView() -> some View {
if someDataState {
PopulatedHomeScreen()
} else {
EmptyView()
}
}
}

thanks for checking out !