I was wondering if someone can shed some light around this:
1- why some SwiftUI Views have body = Swift.Never.
2- is there a general rule for that? for example List().body = Swift.Never, while Button().body is not.
3- Also, by the same token, why in struct _ViewModifier_Content below, the struct needed to have body == Swift.Never?
Let's say you create a custom View
:
struct MyView: View {
var body: some View {
Button(action: { }) {
Text("Hello")
}
}
}
And at some point SwiftUI needs to draw a MyView
on the screen. How does it know what to draw? It asks the MyView
for its body
, and gets some sort of Button
. So now SwiftUI needs to draw the Button
. So it asks the Button
for its body
and gets back some sort of View
. And then it has to ask that button body for its body, and so on, ad infinitum, because every View
has a body
that is itself a View
.
The way Swift solves this is quite clever. Each nested call to body
takes half as long as the prior call, so if asking for MyView
's body takes 1 microsecond, then asking for the Button
s body takes 1/2 of a microsecond, and asking for the Button
's body's body takes 1/4 of a microsecond, and so on, and this is a geometric series whose sum is 2 microseconds, so it only takes Swift 2 microseconds to make the infinite number of body requests needed to draw MyView
.
That last paragraph was just my little joke, ha ha. Ignore it. Here's the real answer.
Every View
must have a body
whose value is itself some sort of View
, but SwiftUI can't really make an infinite number of calls to draw a view, so it has to cut the recursion short somehow. To end the recursion, SwiftUI has a variety of āprimitiveā views that it can draw without asking for body
. These include āatomicā views like Text
, Image
, and Spacer
that don't have subviews, but it also includes some compound views like List
and VStack
that do have subviews.
SwiftUI is never going to ask one of these primitive views for its body, and nobody else should ask for it either, because there is no good value to return. But the View
protocol still requires each one to define a Body
type and a body
property.
The Swift standard library defines a good type to use for situations like this: Never
. There are no values of the Never
type. (If you want to sound like a type theorist, tell your friends it's āuninhabitedā.) If you see a function that takes a Never
argument, you cannot call the function, and if you see a function or a property that returns Never
, you know it cannot return at all. It will either run forever, or crash the program.
So first, SwiftUI makes Never
conform to View
, something like this:
extension Never: View {
typealias Body = Never
var body: Never { fatalError() }
}
And then, as you discovered, SwiftUI uses Never
as the Body
for its primitive views.
Since Button
has a non-Never
Body
, we can deduce that it is probably not a primitive view. It might nevertheless have some special handling, but we can guess that SwiftUI probably does ask a Button
for its body
when it needs to draw the Button
.
As for _ViewModifier_Content
, this is also a type that conforms to View
, but it must be a SwiftUI primitive. SwiftUI is never going to ask a _ViewModifier_Content
for its body
, and you shouldn't either, so its Body
type is Never
.