Understanding SwiftUI View

View is one of the foundational blocks of SwiftUI. View in SwiftUI is a pure UI element moving out of the conventional mix of View and Controller in UIKit. In this blog, we will walk through the SwiftUI View and understand about it.

Why is View a struct and not a class anymore?

Why??

One of the fundamental principles of SwiftUI is to have a single source of truth in your code. A traditional UIKit approach of having a base UIView class and all other views inherit from it has a demerit wherein we have multiple stored properties in our view inherited from parent UIView. You only define properties that you want to use unlike in UIKit where you inherit a number of properties from your parent view.

SwiftUI tends to make views pretty lightweight and what better option than a value type with no headache of reference counting and retain cycles. Reference types are messier to maintain. You can alter your properties from anywhere in your code. Swift UI also makes you be responsible for any views and their own state. Views are independent and isolated from one another. Any changes to a particular view will not affect any other unless they are binded by some common source of truth. It’s allocated on the stack, and it’s passed by value.

Body of a SwiftUI View

Let us open up the View protocol

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

The View protocol has an associated type Body which is constrained to be any type conforming View protocol. The is your actual content that will be rendered on screen. SwiftUI will infer the associated type by the implementation of the body computed property. Let us look at the implementation.

struct ContentView: View {
var body: some View {
VStack {
Text("Hello Swifty !!")
Text("I am amazing !!")
}
}
}

What is the some keyword here?

some keyword is to signify that the return type for the computed property is an Opaque Type. Opaque types keep the caller of your property ignorant of the concrete type.

What if you do not want to use some View?

You can also provide a concrete type for the body. No one will say a NO. In our above case, the type of our body is inferred by SwiftUI to be . So if you want your body to have a concrete type, you have to write the type as . But every time you add any other views in your stack, you have to update the type. Isn’t that painful to do?

self is immutable in View body

One of our common requirements is to maintain certain properties in our code as in the below example we have as one of our dependent properties.

struct ContentView: View {
var title: String
var body: some View {
VStack {
Button(action: {
self.title = "I am changing this"
}) {
Text("Hit me")
}
}
}
}

In the above code, we are trying to mutate one of the source of truth for this view i.e title. Why did we tell the title as a source of truth? Because any properties on which our view depends is a source of truth for our view. We will get an error for the above code:

Cannot assign to property: ‘self’ is immutable

This error is because the SwiftUI view is a computed property and we can not mutate self from within a computed property. The body can not mutate the struct that contains it.

How to fix this?

Every mutable source of truth should be marked with property.

@State var title: String

Making title as a property makes the SwiftUI framework responsible for its storage as well as its mutation.

What happens if we make SwiftUI view as a class?

If you are as curious as I am, you would have definitely thought about why is SwiftUI view always a struct? Can we not make it a class?

You can go ahead and implement View as a class. If possible implement this in a fresh new SwiftUI project.

class ContentView: View {
var body: some View {
VStack {
Text("")
}
}
}

The compiler will throw an error as below:

Protocol ‘View’ requirement ‘_makeView(view:inputs:)’ cannot be satisfied by a non-final class (‘ContentView’) because it uses ‘Self’ in a non-parameter, non-result type position

What is the above error?

Let us try to reproduce this error by a small understandable example in the playground.

protocol Producable {
var producedFactory: Factory<Self>? { get }
}
class Factory<T: Producable> {}class PharmacyProduct: Producable {
weak var producedFactory: Factory<PharmacyProduct>? {
nil
}
}

Here we are creating a factory which can produce a certain product which conforms to . The product has a weak reference to the factory just in case we need to track the factory from which it was produced.

The Factory class intakes a generic constrained i.e any product conforming to the protocol. The protocol needs that the Factory should hold the conforming type in . As soon as you do this, the compiler is going to hit you with a similar error as we saw in our view example earlier.

class Paracetamol: PharmacyProduct {}

Let us create a child class of , the class. class will have a property intaking its generic requirement as instead of . This is the reason why the compiler shoots you an error to make it final to avoid confusion for determining the type to which will point to.

SwiftUI View protocol has something similar requirements, which doesn’t allow you to conform to a non-final class.

Now let us obey the compiler and make our view as a final class.

final class ContentView: View {
var body: some View {
VStack {
Text("")
}
}
}

Run the code and it compiles successfully, but as soon as your simulator is lit, the program will crash.

Crash !!

From the thread hierarchy, you can see that our code has crashed in the . So either way, SwiftUI will not allow us to bypass the view as a class.

We have understood some basic concepts of SwiftUI view. SwiftUI brings in a new declarative way to write your views and along with some important modifications to how we manage our data dependencies. WWDC videos will be great sources to start on this.

I would love to hear from you

You can reach me for any query, feedback, or just want to have a discussion by the following channels:

Twitter — @gabhisek_dev

LinkedIn

Software Developer(iOS), Speaker & Writer at Swift India