SwiftUI —Why should you write as many custom views for better performance

Mohammed Imthathullah
4 min readFeb 18, 2020

SwiftUI is everywhere in the Apple world. Developers are playing with it all the time, and here is my observation on how the view renders and what you should do for better performance. Critiques are welcome.

The SwiftUI’s declarative style offers us many options on how we write the UI code. Even though I’ve always preferred writing UIKit over Interface Builder / StoryBoard, writing SwiftUI takes some time to get used to. Once you get hold of it, it's so intuitive. It’s a paradigm shift we’ve to prepare ourselves for.

Here’s a sample view with a TextField, a Toggle Switch, a Picker which is segmented which changes the value in the below Text and a Button having a simple Image as label.

I hope the code is self explanatory. Here is how it looks.

Now, place breakpoints on the four top-level elements in the VStack TextField, Toggle, Picker and HStack. Run the app and try interacting with all the elements in the View. What do you notice?

When we type something in the TextField, or switch the Toggle or press the small Button no breakpoint is reached. But when we change the Picker, all breakpoints are hit, meaning that the entire view is redrawn. It is reasonable that the HStack below the picker is redrawn. But why the entire view? Maybe SwiftUI is not mature enough to find out where we have use the selected value from picker in our view.

Now, go ahead and change the line #29 from Text(texts[selected]) to Text("Sample text") .

Run the app and interact with the elements again, of course with the breakpoints. Can you notice that the view is not redrawn when we change the Picker?

SwiftUI knows when a child view bound with variable from its parent view affects any of the sibling views, but sadly doesn’t know which sibling view so it redraws the entire parent view.

Let me give you another example to show case this. At the end of the Button, add .disabled(isSwitchOn) . Run the app again and test. This time, the entire view is redrawn when we flip the Toggle switch. You get the point, right?

So, to reduce the number of times a view is redrawn, it is better to create custom child views, which are bounded to @State variables from super view but doesn’t affect any of its other sibling views.

First lets look at the TextField. It is a standalone view with a few function calls chained to it. When the Toggle switch or Picker is changed, even though we can assume that TextField is not redrawn, but the function calls are done again. So lets move the TextField and the chained calls into a custom view.

struct MyTextField: View {
var placholder: String
@Binding var value: String

var body: some View {
TextField(placholder, text: $value)
.padding(EdgeInsets(top: 8, leading: 16,
bottom: 8, trailing: 16))
.background(Color(UIColor.secondarySystemFill))
.cornerRadius(4)
}
}

Now, place a break point inside the body of the MyTextField and replace the TextField in line #13, and its chained function calls with a declaration of MyTextField like below.

MyTextField(placholder: "Enter your name", value: $textFieldValue)

Now when we flip the switch, the parent view is redrawn. But MyTextField is not redrawn. It just goes ahead with the previous instance of MyTextField. SwiftUI compares views, which all conform to Equatable and decides whether to call the body again. In this case, as MyTextField has not changed, so not redrawn.

We can provide custom implementations for Equatable in any view, but we’ll discuss that another day.

Also, if the Picker and the HStack below are related to each other but not to any other sibling view, we can create a Custom View with the two. In such case, just like when the TextField changes and the parent view remains the same, changes to the Picker will affect only the new custom child view and not the parent view.

I leave it to you to try that out.

TL:DR; We have now arrived at two cases, where it is better to create Custom Views. First, when we don’t want the multiple chaining function calls to happen over and over again when other parts of the super view changes. Second, when two siblings are related to each other but not to the other siblings in the parent view.

Happy Swifting!

--

--

Mohammed Imthathullah

Aspiring Author. Mostly writes code and sometimes articles. Tweets @imthath_m