RenderMeThis

2.0.0

RenderMeThis is a simple SwiftUI debugging tool that reveals exactly when your views re‑render or re-compute
Aeastr/RenderMeThis

What's New

Visualizing SwiftUI's True Rendering Behavior

2025-04-25T03:14:26Z

What's New

RenderMeThis has been redesigned with two powerful debugging tools that give you better visibility into SwiftUI's rendering cycle:

🎨 debugRender() - NEW!

Track actual UI redraws with colorful backgrounds that change on each redraw. This helps you identify which views are truly being redrawn by SwiftUI, not just recalculated.

đź”´ debugCompute() (formerly checkForRender)

Visualize when views are recomputed/reinitialized with brief red flashes. This shows you when SwiftUI creates entirely new view instances, which is critical for performance optimization.

Why This Matters

The previous version only showed when views were reinitialized, but couldn't distinguish between actual UI redraws. Now you can:

  • See the difference between a view being recomputed and actually redrawn
  • Target performance optimizations more precisely
  • Understand SwiftUI's diffing system better with visual feedback
  • Apply debugging to individual components or entire view hierarchies

Both tools are available as modifiers (.debugRender(), .debugCompute()) and as wrapper views (DebugRender{}, DebugCompute{}), giving you complete flexibility in how you debug your SwiftUI apps.

As always, all debugging code is conditionally compiled with #if DEBUG, ensuring zero impact on your production builds.

RenderMeThis Logo

RenderMeThis

A simple SwiftUI debugging tool that reveals exactly when your views re-render/compute.
Compatible with iOS 13.0 and later, macOS 10.15 and later


Overview

RenderMeThis is a SwiftUI debugging utility that helps you pinpoint exactly when your views re-render or re-compute.

SwiftUI re-computes a view’s body whenever its state changes, but that doesn’t mean it rebuilds the entire UI. Instead, SwiftUI uses a diffing system to compare the new view hierarchy with the old one, updating only the parts that have actually changed.

RenderMeThis let's you see re-computes (aka re-initalizations) as well as actual re-renders, where the UI is rebuilt


Installation

Swift Package Manager

  1. In Xcode, navigate to File > Add Packages...
  2. Enter the repository URL:
    https://github.com/Aeastr/RenderMeThis
  3. Follow the prompts to add the package to your project.

Usage

Debugging vs. Production

Important: RenderMeThis is a development utility intended solely for debugging purposes. The debug tools are conditionally compiled using Swift’s #if DEBUG directive. This means that in production builds, the debugging code is automatically excluded, ensuring that your app remains lean without any unintended visual effects or performance overhead.

Please ensure that your project’s build settings correctly define the DEBUG flag for development configurations. This will guarantee that the render debugging features are active only during development and testing.

debugRender()

The debugRender() modifier visualizes when SwiftUI re-renders a view by applying a random colored background that changes on each re-render. This is incredibly useful for identifying which parts of your UI are being re-rendered when state changes.

Basic Usage

Text("Hello World")
    .padding()
    .debugRender()

This applies a semi-transparent colored background to the text. The key is that this color will change whenever the view is re-rendered.

With State Changes

When state changes cause a view to re-render, the color will change, making it immediately obvious which views are affected:

struct CounterView: View {
    @State private var count = 0
    
    var body: some View {
        VStack {
            // This text will show a changing background color whenever count changes
            Text("Count: \(count)")
                .padding()
                .debugRender()
            
            Button("Increment") {
                count += 1
            }
        }
    }
}

In this example, every time the button is tapped, the Text view's background color will change because it depends on count.

Selective Debugging

You can apply the modifier to specific views to see exactly which ones are being re-rendered:

VStack {
    Text("This will re-render: \(count)")
        .debugRender() // This background will change color
    
    Text("Static text")
        .debugRender() // This background will NOT change color
    
    Button("Increment") {
        count += 1
    }
    .debugRender() // This only changes when the button itself re-renders
}

Wrapper Approach

The wrapper version only applies to the container, not its children:

// Only the VStack container gets the colored background
DebugRender {
    VStack {
        Text("Last updated: \(Date().formatted())")
        Text("Current count: \(count)")
        Button("Reset") { count = 0 }
    }
}

To debug every element individually, apply the modifier to each component:

VStack {
    Text("Updated: \(Date().formatted())")
        .debugRender() // Gets its own color
    
    Text("Count: \(count)")
        .debugRender() // Gets a different color
    
    Button("Reset") { count = 0 }
        .debugRender() // Button gets its own color
}

debugCompute()

The debugCompute() modifier highlights when SwiftUI recreates/reinitializes a view by briefly flashing it red. This shows when a view is being re-computed/re-initailized

Basic Usage

Text("Hello World")
    .padding()
    .debugCompute()

This will flash red whenever the view is recreated (not just re-rendered).

Interactive UI Elements

Text fields are particularly interesting to debug, as they re-compute on every keystroke:

@State private var searchText = ""

TextField("Search...", text: $searchText)
    .padding()
    .debugCompute() // Will flash red with EVERY keystroke

The text field flashes because SwiftUI creates a new view instance for each character typed.

Dependent Views

Views that depend on changing state will flash when that state changes:

Text("Searching for: \(searchText)")
    .padding()
    .debugCompute() // Flashes whenever searchText changes

Conditional Content

Toggling visibility creates entirely new view instances:

@State private var showDetails = false

Button("Toggle Details") {
    showDetails.toggle()
}
.debugCompute() // Flashes when tapped

if showDetails {
    VStack {
        Text("These are the details")
        Text("More information here")
    }
    .padding()
    .debugCompute() // Flashes when appearing/disappearing
}

Nested vs Non-Nested Usage

Applying to a container affects the whole container's behavior:

// The entire structure flashes on each keystroke
VStack {
    TextField("Type here", text: $searchText) 
    Text("Preview: \(searchText)")
    Button("Clear", action: { searchText = "" })
}
.debugCompute() // Entire VStack reinitializes with each keystroke

Versus targeting specific components:

VStack {
    // Only this component flashes with each keystroke
    TextField("Type here", text: $searchText)
        .debugCompute()
    
    // Only flashes when searchText changes
    Text("Preview: \(searchText)")
        .debugCompute()
    
    // Only flashes when tapped
    Button("Clear") { searchText = "" }
        .debugCompute()
}

Wrapper Approach

Like debugRender(), the wrapper only applies to the container:

DebugCompute {
    VStack {
        Text("Header")
        Text("Content: \(searchText)")
    }
} // Only the VStack container flashes red, not each child view

These visualizations help you understand SwiftUI's view lifecycle and optimize your code for better performance by identifying unnecessary view recreations.


Key Components

  • DebugRender
    A SwiftUI wrapper that uses a Canvas background to draw a random-colored, semi-transparent overlay each time a view is re-rendered. This provides a clear visual indication of which views are actually being re-rendered by SwiftUI's rendering system.

  • DebugCompute
    A debugging wrapper that briefly flashes a red overlay when a view is re-computed/re-initialized (not just re-rendered).

  • Extension Methods
    Two extension methods on View:

    • .debugRender() - Shows re-renders with changing colored backgrounds
    • .debugCompute() - Shows re-computs/re-initalizations with red flashes

These components work together to provide a comprehensive visual debugging system for SwiftUI, helping developers understand both the rendering and computation aspects of SwiftUI's view lifecycle.

_VariadicView Back‑Deployment

RenderMeThis leverages SwiftUI’s internal _VariadicView API to backport its render-check functionality on pre‑iOS 18 and pre‑macOS 15 systems. On iOS 18 and macOS 15 (and newer), we use SwiftUI’s native Group(subviews:transform:) API, but to support older OS versions we expose _VariadicView in the RenderCheck wrapper.

When running on older platforms, RenderCheck wraps its child views inside a _VariadicView.Tree with a custom _RenderCheckGroup layout. This layout iterates over each child view and applies the debugRender() modifier, ensuring that render-checking is supported even on devices running older OS versions.

Note: The use of _VariadicView is strictly limited to pre‑iOS 18 and pre‑macOS 15 environments. On newer systems, we rely on the native APIs.


License

RenderMeThis is available under the MIT license. See the LICENSE file for more information.

Description

  • Swift Tools 5.9.0
View More Packages from this Author

Dependencies

  • None
Last updated: Tue May 13 2025 13:10:28 GMT-0900 (Hawaii-Aleutian Daylight Time)