Understanding _VariadicView
swift SwiftUI iOS _VariadicView Estimated reading time: 8 minutesSwiftUI introduces several powerful types for building dynamic and composable user interfaces. Among these is _VariadicView
, an often overlooked yet highly useful internal type.
This article explores _VariadicView
and related types, their relationships, and practical applications. While _VariadicView
is not part of the public API, understanding it can provide insights into advanced SwiftUI techniques.
Introduction
Recently I faced with some library that build dynamically buttons in vertical or horizontal manner with specific separators in between. The interesting point was that all buttons user by the library should be provided as @ViewBuilder
block packed in special View
type, like Group
. The trick moment here - is that this library somehow disasseble this @ViewBuilder
block and work with each element separately.
So I dive deeper in the library and found, that under the hood there is some (looks like private) type named _VariadicView
and the whole family with it working hard to make this idea come true.
Ok, ok, u may say, that how about this Group
initializer init(subviews:transform:)
:
Group(subviews: content) { subviews in
... // iterate over each subview
Yes, but this is for iOS 18 and Swift 6.0+… how about earlier versions? The answer is _VariadicView
.
As result, we can use this:
_VariadicView.Tree(ContentLayout()) {
content
}
where all magic is inside ContentLayout
:
func body(children: _VariadicView.Children) -> some View {
HStack(spacing: 0) {
children.first
ForEach(children.dropFirst()) { child in
if !hideDivider {
Divider()
}
child
}
}
So, at the first look - same code as we used before, but with _VariadicView
. What’s the benefit? The idea behind this is a bit more deeper - we can easelly access to elements of layout, and we can control and manage them.
_VariadicView
family
_VariadicView
facilitates dynamic rendering of views based on a variable number of child views. Depending of user layout that is processed with _VariadicView.Tree
the way how internal subviews are handled - different.
The family of this type contains next:
public typealias Root = _VariadicView_Root
public typealias ViewRoot = _VariadicView_ViewRoot
public typealias Children = _VariadicView_Children
public typealias UnaryViewRoot = _VariadicView_UnaryViewRoot
public typealias MultiViewRoot = _VariadicView_MultiViewRoot
public struct Tree<Root, Content> where Root: _VariadicView_Root
The idea of how this works can be obtained from OpenSwiftUI project
I also found one of the few posts about this type, that describe, I believe, same piece of code
Let’s review each type in a bit more details.
_VariadicView_Root
_VariadicView_Root
is a typealias or internal construct within SwiftUI
that acts as the abstract representation of a variadic view’s root.
It is used internally by SwiftUI
to manage the root processing of variadic views, delegating to either _VariadicView_MultiViewRoot
or _VariadicView_UnaryViewRoot
depending on the context.
_VariadicView_ViewRoot
_VariadicView.ViewRoot
is the core protocol that defines the behavior of a custom variadic view. Both _VariadicView_MultiViewRoot
and _VariadicView_UnaryViewRoot
conform to this protocol and specialize it for handling multiple or single child views, respectively.
While
_VariadicView.ViewRoot
can be used directly,SwiftUI
provides_VariadicView_MultiViewRoot
and_VariadicView_UnaryViewRoot
for common cases. However, there are scenarios where you may prefer_VariadicView.ViewRoot
:
- Completely Custom Layouts: For layouts not easily expressed with stacks, grids, or predefined patterns.
- Special Child Processing: When you need to implement unique behavior or processing logic for children.
- Debugging or Wrapping Existing Views: Custom wrappers that process child views dynamically
Feature | _VariadicView_ViewRoot |
_VariadicView_Root |
---|---|---|
Type | Protocol | Internal construct or typealias |
Purpose | Defines how variadic views process children | Abstract root representation for variadic views |
Developer Interaction | Developers conform to it to create custom variadic views | Not directly accessible or used by developers |
Usage | Custom layouts and child processing | Framework-level abstraction |
Examples | CustomStack, CustomGrid, etc. | Internally links to _VariadicView_MultiViewRoot and _VariadicView_UnaryViewRoot |
Flexibility | Fully customizable | Internally managed by SwiftUI |
_VariadicView_UnaryViewRoot
- Represents the root of a tree that processes a single variadic view.
- Acts as a point where layout and modifiers are applied to the child views.
struct MyUnaryViewRoot: _VariadicView.UnaryViewRoot {
func body(children: _VariadicView.Children) -> some View {
VStack {
...
}
}
}
_VariadicView_MultiViewRoot
- Represents the root of a tree that processes a few dynamic views.
- Acts as a point where layout and modifiers are applied to the child’s views.
struct MyMultiViewRoot: _VariadicView_MultiViewRoot {
func body(children: _VariadicView.Children) -> some View {
ForEach(children) { child in
HStack {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
child
}
.padding()
}
}
}
struct IndependentChildrenExample: View {
var body: some View {
MyMultiViewRoot {
Text("Child 1")
Text("Child 2")
Text("Child 3")
}
}
}
Feature | _VariadicView.ViewRoot |
_VariadicView_MultiViewRoot |
_VariadicView_UnaryViewRoot |
---|---|---|---|
Purpose | Generic protocol for variadic views | Specializes for multiple children | Specializes for single child |
Flexibility | Highly flexible, requires manual work | Optimized for common multi-view cases | Simplified for single-child cases |
Use Case | Custom layouts or processing | Dynamic layouts like grids or stacks | Single-child wrappers |
Ease of Use | Requires detailed implementation | Straightforward for multi-view layouts | Straightforward for single-child use |
_VariadicView_Children
- Represents a collection of child views in the variadic tree.
- Provides APIs for accessing and iterating over child views.
_VariadicView.Tree(MyUnaryViewRoot()) {
Text("Child 1") // <- part of _VariadicView.Children
Text("Child 2") // <- part of _VariadicView.Children
Text("Child 3") // <- part of _VariadicView.Children
}
_VariadicView.Tree<Root, Content>
_VariadicView.Tree
handles views with a dynamic number of children, such as views constructed with@ViewBuilder
or container views likeHStack
andVStack
.- It organizes child views and integrates them into SwiftUI’s rendering and layout systems.
When constructing UI with containers or builders, SwiftUI needs to manage the child views dynamically. _VariadicView.Tree
handles this task by:
- Organizing the child views into a manageable structure.
- Calculating layouts and ensuring state consistency across dynamic updates.
A few more types (listed above) helps _VariadicView.Tree
to achieve it’s goal.
Type’s diagram
To make things even more clear, here is the relationsheep in graphical way:
+---------------------------+
| _VariadicView |
+---------------------------+
|
v
+---------------------------+
| _VariadicView.ViewRoot |
+---------------------------+
|
v
+---------------------------+
| _VariadicView.Children |
+---------------------------+
|
v
+---------------------------+
| _VariadicView.Tree |
+---------------------------+
|
|
+------------------+
| |
v v
+-------------------+ +-------------------+
| _VariadicView_ | | _VariadicView_ |
| UnaryViewRoot | | MultiViewRoot |
+-------------------+ +-------------------+
Legend:
_VariadicView
: Acts as the foundation, enabling dynamic child processing._VariadicView.ViewRoot
: Represents the root of a variadic view hierarchy._VariadicView.Children
: Provides access to the child views._VariadicView.Tree
: A specialized structure representing the tree of child views in the hierarchy. It ensures efficient organization and traversal of views. Without_VariadicView.Tree
, the system would need to repeatedly iterate through all child views to perform common tasks like rendering, layout computation, or updates, resulting in significant performance overhead and more complex logic.SwiftUI._VariadicView_UnaryViewRoot
: Handles cases where the variadic root processes a single child view.SwiftUI._VariadicView_MultiViewRoot
: Handles cases where the variadic root processes multiple child views.
A good example can explain even better. As a simple, yet powerfull example we can use implementation from so question with small modification:
CustomVStack_MultiViewRoot {
Text("CustomVStack_MultiViewRoot").bold()
parts
}
.border(.red)
The full code is here
The real-world usage can be achieved via various extenstions, such as the one proposed by crhis.eidhof
Alternatives
While _VariadicView.Tree
and its related types are private, developers can achieve similar functionality using public APIs:
@ViewBuilder
struct ExampleView: View {
var body: some View {
VStack {
Text("Child 1")
if Bool.random() {
Text("Conditional Child")
}
Text("Child 3")
}
}
}
ForEach
struct ForEachExample: View {
let items = ["Child 1", "Child 2", "Child 3"]
var body: some View {
VStack {
ForEach(items, id: \.self) { item in
Text(item)
}
}
}
}
ForEach(subviews:content:)
and other similar
HStack(spacing: 0) {
Group(subviews: content) { subviews in
...
}
}
Custom View Containers
struct CustomContainer<Content: View>: View {
let content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
HStack {
content()
}
}
}
struct Example: View {
var body: some View {
CustomContainer {
Text("Child 1")
Text("Child 2")
}
}
}
Best Practices
To produce efficient code that works well on different swift versions and iOS versions, we can use macroses and preconditions:
@available(iOS, introduced: 14.0, deprecated: 18.0, message: "Use `ForEach(subviewOf:content:)` instead")
@MainActor
struct MyLayout: _VariadicView_ViewRoot {
#if swift(>=6.0)
func body(children: _VariadicView.Children) -> some View {
HStack(spacing: 0) {
ForEach(children) { child in
child
}
}
}
#else
nonisolated
func body(children: _VariadicView.Children) -> some View {
HStack(spacing: 0) {
ForEach(children) { child in
child
}
}
}
#endif
}
// and later in some View
public var body: some View {
#if swift(>=6.0)
if #available(iOS 18.0, *) {
HStack(spacing: 0) {
Group(subviews: content) { subviews in
ForEach(subviews) { child in
child
}
}
}
} else {
_VariadicView.Tree(MyLayout()) {
content
}
}
#else
_VariadicView.Tree(MyLayout()) {
content
}
#endif
Conclusion
Although _VariadicView
is an internal API, its concepts and functionality reveal the power and flexibility of SwiftUI. By mastering these types, developers can build more dynamic and reusable UI components, pushing the boundaries of SwiftUI.
Share your thoughts and experiments in the comments below!
Resources
Share on: