Optimizing and Testing View Redraws in SwiftUI

Frank Goortani
3 min readJun 22, 2023

--

Optimizing and Testing View Redraws in SwiftUI

SwiftUI has gained significant popularity due to its declarative syntax and automatic UI updates. However, these automatic updates can sometimes lead to inefficient rendering if not managed correctly. The key to maintaining performance is to prevent unnecessary redraws of views. This article explores techniques to achieve this and how to write tests to ensure your views update as expected.

Preventing Unnecessary Redraws

There are several strategies for preventing unnecessary redraws in SwiftUI.

Avoid unnecessary state changes: SwiftUI redraws views in response to their state changes. Therefore, avoid changing the state of a view unless necessary. Additionally, ensure you’re not updating the state in a way that inadvertently creates a loop of redraws.

Use ‘EquatableView’: By conforming to the EquatableView protocol when creating your custom SwiftUI views, you can define an Equatable conformance that SwiftUI uses to determine if a redraw is necessary. If your custom view has a @State property, you can define an Equatable conformance that checks whether the new state is different from the old state.

struct MyView: View, EquatableView {
@State private var count: Int

var body: some View {
Button("Increment") {
count += 1
}
}
}

extension MyView: Equatable {
static func == (lhs: MyView, rhs: MyView) -> Bool {
lhs.count == rhs.count
}
}

In this example, SwiftUI will only redraw the view when count changes.

Use ‘id(_:)’: You can attach an ID to a view in SwiftUI, triggering a redraw only when the ID changes. This is particularly useful when you have a list of items, with only a few that change.

ForEach(items, id: \.self) { item in
MyView(item: item)
.id(item.id)
}

In this example, SwiftUI only redraws MyView instances when their corresponding item.id changes.

Use onAppear and onDisappear effectively: Avoid doing work in onAppear and onDisappear that may cause unnecessary redraws.

By employing these techniques, you can reduce unnecessary redraws, improving your SwiftUI application’s performance.

Testing for View Redraws

Although SwiftUI does not provide first-class support for directly testing view redraws, you can structure your code to test conditions leading to view redraws. One approach to achieving this is the Model-View-ViewModel (MVVM) design pattern. In this pattern, your view models provide the logic that drives your views, which you can then test.

Here’s a simple counter-example, broken down into model, view model, and view:

// Model
class CounterModel {
@Published var value: Int = 0
}

// ViewModel
class CounterViewModel: ObservableObject {
@Published var model: CounterModel

var value: String {
"\(model.value)"
}

func increment() {
model.value += 1
}

init(model: CounterModel) {
self.model = model
}
}

// View
struct CounterView: View {
@ObservedObject var viewModel: CounterViewModel

var body: some View {
VStack {
Text(viewModel.value)
Button("Increment") {
viewModel.increment()
}
}
}
}

In this case, any change to the CounterModel's value would cause the CounterView to redraw. We can write a unit test to ensure that the increment method in the CounterViewModel changes the value:

func testIncrement() {
let model = CounterModel()
let viewModel = CounterViewModel(model: model)

let initialValue = model.value

viewModel.increment()

XCTAssertNotEqual(model.value, initialValue, "Model value should change after incrementing")
}

If this test passes, you can confidently say that calling the increment the method will cause the CounterView to redraw.

One bonus tip to see what in each view is changed is to use this:

if #available(iOS 15, *) {
let _ = self._printChanges()
}

In conclusion, while SwiftUI simplifies UI development, understanding, and managing view redraws is essential to maintain performance. By separating logic from views and testing this logic, you can ensure your views update as expected.

--

--

Frank Goortani

Tech and Startup Junkie, Coder, Architect, Product Designer, College Instructor and Husband. P.S, I upped my writing game recently by using AI Research Agents