Understanding SwiftUI: View
Tags: swift, ios, swiftui
Published: 2020-07-12 18:54:46

You'll hear a lot of "Everything is a View" when diving into the SwiftUI world and, in fact, practically everything we deal with it is a View, even things you wouldn't expect as a Color. So, what is a View? What's so special about it?

## The Protocol

A View is actually a quite simple protocol with only one required body property whose type also implements the View protocol, and that's it.

```swift
protocol View {
    associatedtype Body: View
    var body: Self.Body { get }
}
```

The body declares the content and behaviour of a View and that's where we code what we want to be display when it's rendered.

```swift
struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
    }
}
```

## Opaque Type

The some keyword in the body property is very important. It's a feature called Opaque Type and it's designed to hide a concrete type that implements a given protocol. It's actually a sort of compile magic and the same code can be translated to the following one:

```swift
struct ContentView: View {
    var body: Text {
        Text("Hello, World!")
    }
}
```

Opaque types really shine when dealing with mores complex views. Because of the compositional nature of SwiftUI, the complexity of the body type scales very quickly, but it goes unnoticed thanks to the opaque type.

```swift
struct ContentView: View {
    var body: some View {
        HStack {
            Rectangle().fill(Color.green)
            Rectangle().fill(Color.white)
            Rectangle().fill(Color.orange)
        }
        .frame(width: 300, height: 300)
        .border(Color.black)
    }
}
```

Swift requires an explicit type for computed properties an without this ability to hide concrete types, it would be virtually impossible to write SwiftUI code in a practical way.

```swift
struct ContentView: View {
    var body: ModifiedContent<ModifiedContent<HStack<TupleView<(_ShapeView<Rectangle, Color>, _ShapeView<Rectangle, Color>, _ShapeView<Rectangle, Color>)>>, _FrameLayout>, _OverlayModifier<_ShapeView<_StrokedShape<_Inset>, Color>>> {
        HStack {
            Rectangle().fill(Color.green)
            Rectangle().fill(Color.white)
            Rectangle().fill(Color.orange)
        }
        .frame(width: 300, height: 300)
        .border(Color.black)
    }
}
```

## Associated Type

This problem that opaque types solve raises the question of why to use an associated type in the first place. SwiftUI engineers could have chosen a slightly different and simple approach for the View protocol, defining the body property as a simple View.

```swift
protocol View {
    var body: View { get }
}
```

This approach would allow declaring the body type as View, without the need of opaque type, regardless of its complexity.

```swift
struct ContentView: View {
    var body: View {
        HStack {
            Rectangle().fill(Color.green)
            Rectangle().fill(Color.white)
            Rectangle().fill(Color.orange)
        }
        .frame(width: 300, height: 300)
        .border(Color.black)
    }
}
```

The downside of this approach is that SwiftUI relies on static types to optimize the updates. Consider the following example.

```swift
struct ContentView: View {
    
    @State var b: Bool
    
    var body: some View {
        VStack {
            Text("...")
            Rectangle()
                .fill(b ? Color.red : Color.green)
        }
    }
}
```

The body property has a concrete type, hidden by the opaque type. You can check this type by accessing the associated type `Body`.

```swift
print(ContentView.Body.self)
// VStack<TupleView<(Text, _ShapeView<Rectangle, Color>)>>
```

Swift's type system ensures that the type is the same every time the body is computed. This makes it possible to check changes on an individual basis and update only what is needed. 

Giving up the concrete type and using only protocol would mean losing the static type and allowing to return anything as long as it conforms to View. The consequence of this is that it would be necessary to redraw the entire screen with each change or perform a much more complicated operation to detect changes in order to update only what is necessary.

You might think that a body with a static type reduces flexibility but SwiftUI provides ways to define **optional** or **conditional** views while keeping it statically typed.

```swift
struct ContentView: View {
    
    @State var b1: Bool
    @State var b2: Bool
    
    var body: some View {
        VStack {
            if b1 {
                Rectangle()
            }
            if b2 {
                Text("...")
            } else {
                Image("...")
            }
        }
    }
}
```

Although the result on the screen is variable, its type will always be the same and SwiftUI knows the possible outcomes.

```swift
print(ContentView.Body.self)
// VStack<TupleView<(Optional<Rectangle>, _ConditionalContent<Text, Image>)>>
```

## The Infinity Recursion Problem

Any implementation of the View protocol will have the same basic structure.

```swift
struct ContentView: View {
    var body: some View {
        ...
    }
}
```

If you think about it, whatever View returned from the body property has its own body, which is also a View and therefore has a body itself and then we end up with an apparently infinity recursion problem.

```swift
+--------+   body   +--------+   body   +--------+
|  View  | +------> |  View  | +------> |  View  | . . .
+--------+          +--------+          +--------+
```

There must be some way to stop this. There is indeed and most SwiftUI built-in View implementations, such as `Text`, have a special way of stopping this recursion. How? By literally returning `Never` from the body!

> "Never is a proposition that an event doesn’t occur at any time in the past or future. It’s logical impossibility with a time axis; nothingness stretching out in all directions, forever."
(Mattt Thompson)

`Never` is a so-called uninhabited type, which means that it has no values and can't be constructed. It's mostly used in Swift functions that stop execution such as `fatalError(_:file:line:)` and its implementation is as simple as possible.

```swift
enum Never { }
```

Extending Never to the implement the View protocol makes it possible to use this type to define a View whose body should never return anything.

```swift
extension Never: View {
    typealias Body = Never
    var body: Never { fatalError() }
}
```

For most of the basic types defined in SwiftUI there is no point in looking at the body property because the type itself and its other properties provide enough information. `Text`, for example, has a property to store the content to be displayed and that is all the framework needs to know. For types like that, it makes sense that nothing is returned from the body.

```swift
extension Text: View {
    public typealias Body = Swift.Never
}
```

Finally, whenever SwiftUI run into a View with a Never body, it knows when to stop, breaking the recursion in a very simple and clever way.

```swift
struct ContentView: View {
    var body: some Text {
        Text("Hello, World!")
    }
}

+---------------+   body   +--------+   body   +---------+
|  ContentView  | +------> |  Text  | +------> |  Never  |
+---------------+          +--------+          +---------+
```

Text body property hasn't a public access level, but if you try the following code you will get an error.

```swift
ContentView().body.body
// Fatal error: body() should not be called on Text
```

This makes it clear that SwiftUI doesn't actually try to access the body property in cases like this and instead it just type check it.

## Conclusion

Being widely used in SwiftUI as it is, View would be expected to be something complicated but as you can see it is actually very simple. It shows how protocols can be powerful and it is a remarkable example of protocol-oriented programming.

## Recommended Readings

[Understanding Opaque Return Types in Swift](https://swiftrocks.com/understanding-opaque-return-types-in-swift)
[Static Types in SwiftUI](https://www.objc.io/blog/2019/11/05/static-types-in-swiftui/)
[Never](https://nshipster.com/never/)