You didn't include it in your question but I guess the error you're getting when going without the stack is the following?
Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
The error gives you a good hint of what's going on but in order to understand it, you need to understand the concept of opaque return types. That's how you call the types prefixed with the some keyword. I didn't see any Apple engineers going deep into that subject at WWDC (maybe I missed the respective talk?), which is why I did a lot of research myself and wrote an article on how these types work and why they are used as return types in SwiftUI.
There is also a detailed technical explanation in another
If you want to fully understand what's going on I recommend reading both.
As a quick explanation here:
General Rule:
Functions or properties with an opaque result type (some Type)
must always return the same concrete type.
In your example, your body property returns a different type, depending on the condition:
var body: some View {
if someConditionIsTrue {
TabView()
} else {
LoginView()
}
}
If someConditionIsTrue, it would return a TabView, otherwise a LoginView. This violates the rule which is why the compiler complains.
If you wrap your condition in a stack view, the stack view will include the concrete types of both conditional branches in its own generic type:
HStack<ConditionalContent<TabView, LoginView>>
As a consequence, no matter which view is actually returned, the result type of the stack will always be the same and hence the compiler won't complain.
💡 Supplemental:
There is actually a view component SwiftUI provides specifically for this use case and it's actually what stacks use internally as you can see in the example above:
It has the following generic type, with the generic placeholder automatically being inferred from your implementation:
ConditionalContent<TrueContent, FalseContent>
I recommend using that view container rather that a stack because it makes its purpose semantically clear to other developers.
ConditionalContentseems to me a either/or type of struct that gets generated from the compiler when interpreting a@ViewBuilderblock. I think that's how ourifs/elsesinside Groups. Stacks, etc are translated. I think so because it yields aView. In your case, thatif/elsegets translated to aConditionalContent<TabView, LoginView>. - Matteo PaciniSwiftUI, so it will take some time to define abest practice. Code looks good, so go for it! An improvement you could do: have a state in the view to decide whether to show theTabVieworLoginView, and then mutate that state via a view model - via aBinding. - Matteo PaciniHStack { ... }is only used to provide an “outer group” (to make the if-else compile) then you can also useGroup { ... }instead. - Martin Rif/elsein a@ViewBuilderblock yields aConditionalStatementat compiler level: i.imgur.com/VtI4yLg.png. - Matteo Pacini