Reading time: 2 min

This quick tip shows how to speed up your SwiftUI development by simply breaking the code of your Views into smaller chunks. This results in:

  • Faster Source Editor operation, i.e syntax highlighting and error/warnings checks will come in faster.
  • Faster preview build and refresh time.
  • Faster build time of SwiftUI code, both for clean and incremental builds.

It also allows you to work around an issue that you might've seen, which says that:

The compiler is unable to type-check this expression in reasonable time;
try breaking up the expression into distinct sub-expressions.

All of these issues arise because you're putting too much code in the body property. If you just break it up into computed properties (for code that doesn't depend on parameters) or methods (for those that do), you'll see significant performance gains in XCode, plus your code will be easier to read and maintain.

It's generally a good idea to add the @ViewBuilder attribute to those properties/methods, as it allows you to structure your code the same way you do in body.

Consider the following example:

var body: some View {
  NavigationView {
    List {
      ForEach(tasks, id: \.dueDate) {
        TaskRow(task: $0)
      }.onDelete(perform: deleteTask)
      Section(header: Text("Projects")
        .font(.title2)
        .fontWeight(.bold)
        .foregroundColor(Color.pink)) {
        ForEach(projects, id: \.projectTitle) {
          ProjectRow(project: $0)
        }.onDelete(perform: deleteProject)
      }
    }.toolbar {
      ToolbarItem (placement: .primaryAction) {
        EditButton()
          .padding(10)
          .background(Color.blue)
          .foregroundColor(.white)
          .cornerRadius(10)
      }
      ToolbarItem (placement: .bottomBar) {
        Button(action: addTask) {
          Text("Add Task")
            .fontWeight(.semibold)
        }.padding(10)
          .background(Color.blue)
          .foregroundColor(.white)
          .cornerRadius(10)
      }
    }
    .sheet(isPresented: $isPresenting) {
      if isAddingProject {
        AddProject() { projectTitle in
          addProject(projectTitle: projectTitle)
        }
      }
    }
    .navigationBarTitle(Text("Taskmaster"))
  }
}

To gain all the benefits mentioned above, try organizing it like this:

var body: some View {
  NavigationView {
    taskList
    .toolbar {
      editButton
      addButton
    }
    .sheet(isPresented: $isPresenting, content: sheetContent)
    .navigationBarTitle(Text("Taskmaster"))
  }

  @ViewBuilder var taskList: some View {
    List {
      ForEach(tasks, id: \.dueDate) {
        TaskRow(task: $0)
      }.onDelete(perform: deleteTask)
      Section(header: Text("Projects")
        .font(.title2)
        .fontWeight(.bold)
        .foregroundColor(Color.pink)) {
        ForEach(projects, id: \.projectTitle) {
          ProjectRow(project: $0)
        }.onDelete(perform: deleteProject)
      }
    }
  }

  @ViewBuilder var editButton: some View {
    ToolbarItem (placement: .primaryAction) {
      EditButton()
        .padding(10)
        .background(Color.blue)
        .foregroundColor(.white)
        .cornerRadius(10)
    }
  }

  @ViewBuilder var addButton: some View {
    ToolbarItem (placement: .bottomBar) {
      Button(action: addTask) {
        Text("Add Task")
          .fontWeight(.semibold)
      }.padding(10)
        .background(Color.blue)
        .foregroundColor(.white)
        .cornerRadius(10)
    }
  }

  @ViewBuilder var sheetContent: some View {
    if isAddingProject {
      AddProject() { projectTitle in
        addProject(projectTitle: projectTitle)
      }
    }
  }
}

Next Post Previous Post