Logo for tanaschita.com

How to avoid using AnyView in SwiftUI

By using alternatives such as the @ViewBuilder attribute, Group type or generics.

updated on 01 Apr 2024 · 6 min read

SwiftUI provides the type-erased view AnyView that can be used as a wrapper for any other SwiftUI view, for example to be able to return multiple view types from a function.

While using AnyView might be a valid choice in some cases, we should use alternatives where possible. The reason is that SwiftUI works with a so called structural identity mechanism, which uses the view's type to identify it and to determine when they should be updated. Since AnyView erases the type of the view, it reduces SwiftUI’s ability to efficiently update the view.

There are different possibilities which can help us avoiding AnyView:

  1. Using @ViewBuilder attribute instead of AnyView
  2. Using Group instead of AnyView
  3. Using Generics instead of AnyView

Let's jump in and look at each possibility in more detail.

Sponsorship logo
Preparing for a technical iOS job interview - updated for iOS 17
Check out my book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift, SwiftUI, Combine, HTTP Networking, Authentication, SwiftData & Core Data, Concurrency with async/await, Security, Automated Testing, Machine Learning and more.
LEARN MORE

Using @ViewBuilder attribute instead of AnyView

Let's look at an example where we might be tempted to use AnyView:

private var nameView: some View {
if isEditable {
return TextField("Your name", text: $name)
} else {
return Text(name)
}
}

When defining view properties or functions that return a view, we often use the some View opaque return type so we don't need to explicitly define an exact return type.

But the above code won't compile with the error message Function declares an opaque return type, but the return statements in its body do not have matching underlying types.

To resolve the error and return the same type for both views, we might be tempted to wrap them in AnyView.

private var nameView: some View {
if isEditable {
return AnyView(TextField("Your name", text: $name))
} else {
return AnyView(Text(name))
}
}

But there is a more elegant and more performant solution to solve the compiler error - the @ViewBuilder attribute.

The @ViewBuilder attribute allows us to compose multiple views into a single return type:

@ViewBuilder
private var nameView: some View {
if isEditable {
TextField("Your name", text: $name)
} else {
Text(name)
}
}

All we have to do is to add the attribute to our property or function and remove the return statements.

That's the same mechanism the body of a SwiftUI view uses. The only difference is that we explicitly have to add the ViewBuilder attribute on our own properties and functions.

Using Group instead of AnyView

By using the Group type, we can collect multiple views into a single one without affecting the layout of those views.

private var nameView: some View {
Group {
if isEditable {
TextField("Your name", text: $name)
} else {
Text(name)
}
}
}

Since Group uses ViewBuilder, we can group different kinds of views with conditional statements.

Using Generics instead of AnyView

Another common situation where we might be tempted to use AnyView is when we need to store a view without knowing its type:

struct FlyoutView: View {
let headerView: AnyView
var body: some View {
VStack {
headerView
Spacer()
// other views
}
}
}

In the example above, we want the headerView to be of a flexible type. Since we can't use some View for stored properties, AnyView seems like a good choice.

A more elegant and performant solution would be using generics.

struct FlyoutView<HeaderView> : View where HeaderView : View {
let headerView: HeaderView
var body: some View {
VStack {
headerView
Spacer()
}
}
}

When creating a FlyoutView, we don't need to wrap the header view in AnyView.

FlyoutView(headerView: Text("Header view"))

The same generics approach is used by many views provided by SwiftUI.

struct VStack<Content> : View where Content : View {
}

As we can see above, the content view of a VStack is of a generic type that conforms to View.

Conclusion

Using AnyView can be avoided in most situations. The shown alternatives not only add more performance to our SwiftUI views, they also result in a more elegant and readable code.

Sponsorship logo
Preparing for a technical iOS job interview - updated for iOS 17
Check out my book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift, SwiftUI, Combine, HTTP Networking, Authentication, SwiftData & Core Data, Concurrency with async/await, Security, Automated Testing, Machine Learning and more.
LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

ux

swiftui

swift

xcode

ios

How to support dark mode in SwiftUI programmatically

Learn how to improve type safety when working with semantic colors.

18 Mar 2024 · 4 min read

Latest articles and tips

© 2024 tanaschita.com

Privacy policy

Impressum