Debugging redraws and understanding view identity

Noticing view redraws

One trick I use in my projects is that I introduce a debug only random border into my views, this means that whenever view gets re-render the color of the border changes, this is noticable and let's me investigate further by employing Self._printChanges() to find the culprit.


extension Color {
    static func random() -> Color {
        return Color(red: Double.random(in: 0...1), green: Double.random(in: 0...1), blue: Double.random(in: 0...1))
    }
}

extension View {
    @ViewBuilder
    func debugBorder() -> some View {
        #if DEBUG
        self.border(Color.random)
        #else
        self
        #endif
    }
}
  

Understanding view identity

View identity is core to SwiftUI workings, whenever you see @State variables the way they work underneath is by using global reference store keyed by the view identity, thus when you cause views to loose their identity their data will also get lost.

A no-op modifier is a modifier that is doing nothing to the actual display, like adding .clear background.

In this lesson I show you how you should always prefer no-op modifiers over conditional branches:


// Different Identities
if flag {
   ViewWithState()
       .background(Color.red)
       .animation(.easeInOut(duration: 2), value: flag)
} else {
   ViewWithState()
       .animation(.easeInOut(duration: 2), value: flag)
}
  
// Single identity
// .clear option becomes a no-op 
ViewWithState()
    .background(flag ? .red : .clear)
    .animation(.easeInOut(duration: 2), value: flag)
Complete and Continue