
A simple SwiftUI debugging tool that reveals exactly when your views re‑render.
Compatible with iOS 13.0 and later, macOS 10.15 and later
RenderMeThis is a SwiftUI debugging utility that helps you pinpoint exactly when your views re‑render. By integrating RenderMeThis into your project, each re‑render is highlighted by a brief red flash, making it easier to track down unnecessary view updates and optimize performance. Designed for iOS 13.0 and later, RenderMeThis offers both a modifier-based method and a wrapper-based method for flexible integration into your SwiftUI views.
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. If you break your UI into separate structs and a subview (like a text field) has no state change, it won’t be re-rendered at all—achieving re-render behavior similar to UIKit.
As of now this works by wrapping your code, or using a modifier, but I'm cooking something a bit more cool for later
- In Xcode, navigate to File > Add Packages...
- Enter the repository URL:
https://github.com/Aeastr/RenderMeThis
- Follow the prompts to add the package to your project.
Below are two sets of examples demonstrating how to use RenderMeThis. The first set leverages the wrapper method (using RenderCheck
), available on iOS 13+; the second uses the modifier method (using checkForRender()
) for iOS 13+.
Important: RenderMeThis is a development utility intended solely for debugging purposes. The debug overlay—which highlights view re‑renders with a red flash—is 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.
Wrap your entire view hierarchy with RenderCheck
to automatically apply render debugging to every subview:
import SwiftUI
struct ContentView: View {
@State private var counter = 0
var body: some View {
NavigationStack {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
// Entire content is wrapped in RenderCheck.
RenderCheck {
Text("Main Content")
.font(.headline)
Text("Counter: \(counter)")
.font(.subheadline)
Button(action: {
counter += 1
}) {
Label("Increment", systemImage: "plus.circle.fill")
.padding()
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
}
Divider()
Text("Separate Section")
.font(.headline)
ContentSubView()
}
}
}
.padding()
.navigationTitle("RenderMeThis")
}
}
}
struct ContentSubView: View {
@State private var counter = 0
var body: some View {
VStack(alignment: .leading, spacing: 12) {
RenderCheck {
Text("Counter: \(counter)")
.font(.subheadline)
Button(action: {
counter += 1
}) {
Label("Increment", systemImage: "plus.circle.fill")
.padding()
.background(Color.green.opacity(0.2))
.cornerRadius(8)
}
}
}
}
}
Apply the render debugging effect to individual views using the checkForRender()
modifier:
import SwiftUI
struct ContentView: View {
@State private var counter = 0
var body: some View {
NavigationView {
VStack(spacing: 20) {
VStack(spacing: 12) {
Text("Main Content")
.font(.headline)
.checkForRender()
Text("Counter: \(counter)")
.font(.subheadline)
.checkForRender()
Button(action: {
counter += 1
}) {
HStack {
Text("Increment")
Image(systemName: "plus.circle.fill")
}
.padding()
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
}
.checkForRender()
Divider()
.checkForRender()
Text("Separate Section")
.font(.headline)
.checkForRender()
ContentSubView()
.checkForRender()
}
}
.padding()
}
}
}
struct ContentSubView: View {
@State private var counter = 0
var body: some View {
VStack(spacing: 12) {
Text("Counter: \(counter)")
.font(.subheadline)
.checkForRender()
Button(action: {
counter += 1
}) {
HStack {
Text("Increment")
Image(systemName: "plus.circle.fill")
}
.padding()
.background(Color.green.opacity(0.2))
.cornerRadius(8)
}
.checkForRender()
}
}
}
-
RenderDebugView
A SwiftUI wrapper that overlays its content with a brief red flash each time the view is re‑initialized. This flash indicates that the view has re‑rendered. -
RenderCheck
A convenience wrapper that applies the render debugging effect to multiple subviews. By using@ViewBuilder
, it accepts and wraps multiple views with the render detection modifier. -
LocalRenderManager
An internal utility responsible for managing the flash state. It triggers a temporary red flash by setting a Boolean flag that controls the overlay’s opacity. -
Modifier Method
An extension onView
calledcheckForRender()
which wraps any view in aRenderDebugView
, allowing for quick and simple integration of the render debugging effect.
RenderMeThis leverages SwiftUI’s view refresh cycle to visually indicate when views re‑render. Here’s a breakdown of how the different components work together:
-
Initialization Trigger:
When a view is wrapped inRenderDebugView
, its initializer is called. This creates a new instance ofLocalRenderManager
and immediately triggers the render flash. -
Flash Overlay:
The view content is overlaid with a red color whose opacity is determined by therendered
state fromLocalRenderManager
. Whenrendered
is true, a semi‑transparent red tint (30% opacity) appears and then fades out with an ease‑out animation over 0.3 seconds.
-
State Management:
This manager maintains a Booleanrendered
property that controls the overlay’s visibility. -
Triggering and Reset:
WhentriggerRender()
is called,rendered
is set to true, causing the red flash. A scheduled task resetsrendered
to false after 0.3 seconds, ensuring that the flash is temporary.
- Convenience Wrapper:
RenderCheck
uses SwiftUI’s@ViewBuilder
to accept multiple subviews. It groups them together and applies thecheckForRender()
modifier, thereby enabling re‑render detection across an entire view hierarchy without modifying each individual subview.
- Simplified Integration:
The extension methodcheckForRender()
onView
wraps any view in aRenderDebugView
. This allows you to integrate render detection quickly by simply appending the modifier to your view.
Together, these components allow you to monitor your SwiftUI views for unnecessary or unexpected re‑renders
RenderMeThis is intended for debugging and development purposes. The visual overlay indicating view re‑renders should be disabled or removed in production builds.
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 checkForRender()
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.
RenderMeThis is available under the MIT license. See the LICENSE file for more information.