Skip to content

A debug tool to render the view tree of a SwiftUI view based on the work of Chris Eidhof.

License

Notifications You must be signed in to change notification settings

agrabz/SwiftUIViewTree

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitHub tag (latest by date)

Usage

Use .renderViewTree(of: self) to see the view tree visually next to your original view.

RocketSim_Screenshot_iPhone_17_Pro_6 3_2026-01-05_14 57 07 RocketSim_Screenshot_iPhone_17_Pro_6 3_2026-01-05_14 57 21

The screenshots above is produced by the code below:

import SwiftUIViewTree

struct ContentView: View {
    @State var isTapped = false

    var body: some View {
        Button {
            isTapped.toggle()
        } label: {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundStyle(.tint)
                Text(isTapped ? "Yo what?" : "Hello, world!")
                    .bold(isTapped ? true : false)
            }
            .padding()
        }
        .renderViewTree(of: self) // <--------
    }
}

Without the .renderViewTree(of: self) line the view would be just like:

Simulator Screenshot - iPhone 16 Pro - 2025-09-29 at 10 57 40

Features

Pinch to zoom and scroll

You can also pinch to zoom and scroll to any direction.

Tip: Use Option + Shift to move both fingers on a simulator.

fixed-gif

Double tap to zoom

You can apply stepped zoom by double tapping on the view tree's canvas.

fixed-gif2

Node color change on updates

If something got updated due to a state change it gets a new color:

fixed-gif2

Note: structural changes like myEnum.case1 --> myEnum.case2(let someInteger) in the view tree are not yet handled properly (but it's on the top of the roadmap), only simple value changes: true --> false

Change Detection | Printing to the console

When a node gets a new color you can also see it in the console:

🚨Changes detected in "_value": "Bool"
🟥Old value: "true"
🟩New value: "false"
🔺Diff at [0]: '...true...' --> '...false...'

Double tap a node to Collapse/ Expand

If certain parts of the tree are redundant for you, then you can double tap to collapse and later reopen them:

fixed-gif3

A collapsed node is always gray and a badge indicates how many descendants it has:

RocketSim_Screenshot_iPhone_17_Pro_6 3_2026-01-05_15 47 08

Tap a node to see full details

You might not want to zoom in always to see the details of a specific node. In that case you can tap on any node to print its full details.

Node Details:
    Label: _value
    Type: Bool
    Value: true

Swift 6 Support

The solution is fully compatible with Swift 6.

iPad Support

You can browse the view tree on an iPad as well.

RocketSim_Screenshot_iPad_Air_13-inch_(M3)_12 9_2026-01-05_14 57 56 RocketSim_Screenshot_iPad_Air_13-inch_(M3)_12 9_2026-01-05_14 56 18

Why is this useful?

SwiftUI can produce unexpected updates that are not easy to troubleshoot with the built-in tools.

To troubleshoot and better understand the surprises of SwiftUI you might find this library useful.

There are numerous articles on the web that are about this topic. I'll collect some of my favorites and reference them here as the repository matures.

Roadmap

  • Track structural graph changes properly. E.g. myEnum.case1 --> myEnum.case2(let someInteger)

  • Add history of graph state to be able to track quick changes e.g. image blinking.

  • Detach view tree from source to stop receiving new updates

  • Setable frame for the view tree

  • Tapping on a connection line to scroll to the parent node

  • Better accessibility support, maybe with the usage of OutlineGroup as renderMode

  • Supporting logs for production usage

  • Dedicated documentation page

  • Support for Mac

  • CI on PRs

  • Build a table in the documentation of tested view types - maybe

Alternatives

Probably the closest alternative is using SwiftUI's built-in, documented, but still private APIs:

let _ = Self._printChanges()
let _ = Self._logChanges()

As their name suggests, these APIs do not provide any UI, unlike this library.

Another alternative to figure out the exact type of a SwiftUI.View you can simply change the some View return type of the body computed variable to a definitely wrong type like String.

With that you can see from the compiler error message the concrete type of your view.

In the below example the (more) concrete type is VStack<TupleView<(some View, Text)>>

struct ContentView: View {
    var body: String { // Compiler error: Cannot convert return expression of type 'VStack<TupleView<(some View, Text)>>' to return type 'String'
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
                .font(.largeTitle)
                .bold()
        }
    }
}

This means that you can change the type of the body to VStack<TupleView<(some View, Text)>> and the compiler will be okay with that.

struct ContentView: View {
    var body: VStack<TupleView<(some View, Text)>> { // No error, this compiles
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
                .font(.largeTitle)
                .bold()
        }
    }
}

However you might already see the two limitations of this approach:

  1. There's yet again a some View in the type, so you still don't know the concrete type.

What's worse is that figuring that out is not as straightforward as it was in the above example.

  1. You don't see all the details that are made visible by this package. For example all the properties of the types.

Support

If you face any problems, or have any feature request please feel free to open an Issue.

Contribution

While I'm not new to open source development, I'm new to being the author of a library. Any help is welcome, please feel free to open PRs.

Credits

This project is based on the work of Chris Eidhof (objc.io). In his 2019 Swiftable conference talk called "SwiftUI under the hood" (link), he demoed a helper function which he called .mirror(). His mirror view modifier can be applied to any kind of SwiftUI View and will render its "viewtree" next to it:

image

While his talk is not primarily about this helper function, it got the attention of many people as it seems to be a really promising debugging tool.

Additionally when I started to work on my implementation of his tool, I researched GitHub for any open source Swift projects for building graphs and I found danielctull-playground/TreeView where the author describes the repository as "Implementation following the objc.io TreeView".

I ended up using this repository's implementation with some tweaks, so I have to give the credits again to Chris and the whole objc.io team.

About

A debug tool to render the view tree of a SwiftUI view based on the work of Chris Eidhof.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages