How SwiftUI uses generic modifiers to create views

In this short post I want give the intuition about how SwiftUI views work behind the scenes.

Why SwiftUI uses struct instead of classes?

UIKit or AppKit, use classes for views, and our views descended from a class called UIView.
If you look at the Apple documentation for UIView, you will find more than 200 entries of properties and methods that are passed down to the subclasses whether they need them or not. It is an interesting read! developer.apple.com/documentation/uikit/uiv..

In SwiftUI, all our views are structs and are almost free to create. Structs do not inherit from parent classes, or grandparent classes – they contain just what you can see.

What is a SwiftUI view?

A SwiftUI view is created behind the scenes as a non-mutable struct.
Their performance is better because they are simpler and faster than classes.
Also because SwiftUI views don’t mutate over time, we move to a more functional design approach, the views only convert data into UI, instead of having too many functionalities that can make our app behaving in unexpected ways.

Looking behind our view and we see?

Nothing! there is nothing behind our view. :)
Of course behind our content view, there is a UIHostingController, which is the bridge between UIKit (Apple’s original iOS UI framework) and SwiftUI. However, what we see on the screen is what we got. The mindset here is, if you want to change the size of your view, you pass a modifier which will not modify our existing view, but rather return a new view!
The view we created at first is a struct and does not store additional properties we give, rather returns a new view with those properties.

It will become clear with an example. Let's get the intuition behind how views get modified. Open a new SwiftUI project in Xcode and change our "hello world" template like this:

struct ContentView: View {
    var body: some View {
        Text("Hello World")
            .padding()
            .frame(maxWidth: .infinity)
            .background(Color.pink)
            .onAppear {
                print(type(of: self.body))
            }
    }
}

This will create a simple view displaying "Hello World" on the screen and adding a pink color background. On appearing, when the system will have created our struct, it will print on the console the contents of type(of: self.body), which will show how SwiftUI store our views behind the scene.

This will show that, whenever we apply a modifier to a SwiftUI view, we actually create a new view with that change applied – we don’t just modify the existing view in place.

This is how our app looks like:

ScreenshotHelloWorld.png

The console will print for us the following:

ModifiedContent<ModifiedContent<ModifiedContent<ModifiedContent<Text, _PaddingLayout>, _FlexFrameLayout>, _BackgroundModifier<Color>>, _AppearanceActionModifier>

What we get printed in the console is fascinating to me. It shows that when we modify a view, SwiftUI applies that modifier by using generics: ModifiedContent<OurThing, OurModifier>.

When we apply multiple modifiers, they just stack up: ModifiedContent<ModifiedContent<…

This mean two things:

  • Each modifier takes a view (to transform) and the actual change to make. It does not modify the view properties directly.
  • and also, the order of your modifiers matters! 😀

Generics are used extensively in Swift and allow us to write code that is capable of working with a variety of different types.
If you do not know them the Swift docs are a great starting point:
docs.swift.org/swift-book/LanguageGuide/Gen..

Kudos to Paul Hudson and his Hacking with Swift blog for inspiring this content! hackingwithswift.com/plus