SwiftUI relies very heavily on a Swift power feature called “opaque return types”, which you can see in action every time you write some View
. This means “one specific type that conforms to the View
protocol, but we don’t want to say what.”
Returning some View
has two important differences compared to just returning View
:
The first difference is important for performance: SwiftUI needs to be able to look at the views we are showing and understand how they change, so it can correctly update the user interface. If we were allowed to change views randomly, it would be really slow for SwiftUI to figure out exactly what changed – it would pretty much need to ditch everything and start again after every small change.
The second difference is important because of the way SwiftUI builds up its data using ModifiedContent
. Previously I showed you this code:
Button("Hello World") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(Color.red)
That creates a simple button then makes it print its exact Swift type, and gives some long output with a couple of instances of ModifiedContent
.
The View
protocol has an associated type attached to it, which is Swift’s way of saying that View
by itself doesn’t mean anything – we need to say exactly what kind of view it is. It effectively has a hole in it, in just the same way Swift doesn’t let us say “this variable is an array” and instead requires that we say what’s in the array: “this variable is a string array.”
So, while it’s not allowed to write a view like this:
struct ContentView: View {
var body: View {
Text("Hello World")
}
}
It is perfectly legal to write a view like this:
struct ContentView: View {
var body: Text {
Text("Hello World")
}
}
Returning View
makes no sense, because Swift wants to know what’s inside the view – it has a big hole that must be filled. On the other hand, returning Text
is fine, because we’ve filled the hole; Swift knows what the view is.
Now let’s return to our code from earlier:
Button("Hello World") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(Color.red)
If we want to return one of those from our body
property, what should we write? While you could try to figure out the exact combination of ModifiedContent
generics, it’s hideously painful and the simple truth is that we don’t care: it’s all internal SwiftUI stuff.
What some View
lets us do is say “this will return one specific type of view, such as Button
or Text
, but I don’t want to say what.” So, the hole that View has will be filled by a real view, but we aren’t required to write out the exact long type.
Now, in case you were curious you might wonder how SwiftUI is able to deal with something like VStack
– it conforms to the View
protocol, but how does it fill the “what kind of content does it have?” hole if it can contain lots of different things inside it?
Well, if you create a VStack
with two text views inside, SwiftUI silently creates a TupleView
to contain those two views – a special type of view that holds exactly two views inside it. So, the VStack
fills the “what kind of view is this?” with the answer “it’s a TupleView
containing two text views.”
And what if you have three text views inside the VStack
? Then it’s a TupleView
containing three views. Or four views. Or eight views, or even ten views – there is literally a version of TupleView
that tracks ten different kinds of content:
TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>
And that’s why SwiftUI doesn’t allow more than 10 views inside a parent: they wrote versions of TupleView
that handle 2 views through 10, but no more.
SPONSORED Would you describe yourself as knowledgeable, but struggling when you have to come up with your own code? Fernando Olivares has a new book containing iOS rules you can immediately apply to your coding habits to see dramatic improvements, while also teaching applied programming fundamentals seen in refactored code from published apps.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.