DesignReviewToolkit
📚 Table of Contents
Overview
DesignReviewToolkit takes a SwiftUI View, then generates an image containing it’s Style & Accessibility modifiers. This allows …
DesignReviewToolkit
📚 Table of Contents
Overview
DesignReviewToolkit takes a SwiftUI View, then generates an image containing it’s Style & Accessibility modifiers. This allows you to see everything all at once. You can identify areas where you are missing Accessibility. Image generation is deterministic, so if the underlying style/accessibility data changes, the image will change.
| Original SwiftUI View | Generated (Style+Accessibility) | Generated (Style Only) | Generated (Accessibility Only) | Generated (Size Only) |
|---|---|---|---|---|
Accessibility
We suport modifiers like sortPriority, label, hidden etc. The sortPriority modifier for example has a ID rendered over the top of the view, Hidden renders diagonal red lines.
Style
We support basic style modifiers like, background, foreground style etc. The frame modifier for example lets us render a size and spacing lines over the top of the image.
Quick Start
Follow the steps to generate your first image.
- Add the package
- Add a test
- Run the
PreProcessPlugin supplied by the package - Run your Unit Test (Add breakpoints etc to view the generated image, or write it to disk)
- Run the
CleanPlugin to tidy up modifcations to your source code
Add the Package
Add the package to your Xcode Project or Swift Package
.package(url: "https://github.com/nthState/DesignReviewToolkit", exact: "0.0.1")
Add a Unit Test
Add a Unit Test for the View you wish to capture
import DesignReviewToolkit
@Test() func `ContentView has accessibility annotations`() async throws {
let view = ContentView()
let generator = Generator()
let image = try await generator.generate(from: view)
#expect(image.width > 0 && image.height > 0)
}
Note: Add the following expression to the debugger to see the image inside Xcode
UIImage(cgImage: image)
Image Diff
The Generator creates deterministic images, which means you can diff them, the below code is an example of how one might do it.
@Test() func `ContentView View is the same as a version saved to disk`() async throws {
let view = ContentView()
.environment(\.colorScheme, .dark)
let generator = Generator()
let image = try await generator.generate(from: view)
let testingRoot = URL(fileURLWithPath: "/Users/chrisdavis/Developer/DesignReviewToolkitSample/DesignFilesOutput")
llet comparisonImageURL = testingRoot.appending(path: "ContentView.png")
let imagesMatch = try await generator.hasMatchingData(image, to: comparisonImageURL)
#expect(imagesMatch)
}
Run the Command in Xcode
Then it’s a quick three step process:
- Right click on your project and select
PreProcessunderDesignReviewToolkit - Run your tests (CMD+U)
- Right click on the project and select
CleanunderDesignReviewToolkit
Skipping files
If you want a particular file to be skipped and not pre-processed, you can add this comment in your file.
// designreviewtoolkit:skip
Configuration
You can pass a configuration file to the Generator and CLI.
We try and search for a file called .designreviewtoolkit.json in your root, but you can pass in any file you wish.
The general structure looks like this, all parameters are optional.
{
"version": "0.0.1",
"views": [
"MyCustomSwiftUIView"
],
"spacing": {
"horizontal": 64,
"vertical": 48
},
"includeAccessibility": [
"accessibilitySortPriority",
"accessibilityHidden"
],
"includeStyle": [
"frame",
"background"
],
"showStyle": true,
"showAccessiility": true,
"author": "nthState Ltd",
"convergeLines": false
}
- version: The version of the configuration file
- views: If you have a custom SwiftUI view that you add accessibility/style onto, add them here. By default we scan for default views like VStack, HStack etc
- spacing: Horizontal and Vertical space between the app and the callouts
- includeAccessibility: You may only want certain accessibility items drawn, otherwise show all
- showAccessibility: If you want accessibility callouts drawn
- includeStyle: You may only want certain style items drawn, otherwise show all
- showStyle: If you want style callouts drawn
- author: The name to show in the footer
- convergeLines: Do lines converge on a mid-point, or spread so they are clearer
Continuous Design Review
You may want to let CI handle the generation of the images, if so, here’s a rough outline of a GitHub Action to do it for you.
jobs:
designreview:
name: Design Review
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Pre process files
run: |
swift run --package-path DesignReviewToolkit/Source/DesignReviewToolkitCLI DesignReviewToolkitCLI pre-process files "DesignReviewToolkitSample/DemoView1.swift"
- name: Generate Screenshots
run: |
xcodebuild clean test -project "DesignReviewKitSample.xcodeproj" -scheme "DesignReviewKitSample" -destination "platform=iOS Simulator,name=iPhone 17 Pro Max,OS=latest"
- name: Clean up
run: |
swift run --package-path DesignReviewToolkit/Source/DesignReviewToolkitCLI DesignReviewToolkitCLI clean files "DesignReviewToolkitSample/DemoView1.swift"
I don’t like the design, can I change it?
Yes, You can pass in your own custom SwiftUI views into the generator.
Generator(configuration: Configuration) { String, Configuration in
Text("My Custom Title")
} footer: { Configuration in
Text("My Custom Footer")
} callout: { ItemData, DataType, Configuration in
Text("My Custom CallOut")
} hidden: { ItemData, Configuration in
Text("My Custom Hidden Overlay")
} highlight: { ItemData, Configuration in
Text("My Custom Highlight")
} sortPriority: { Int, Configuration in
Text("My Custom Sort Priority View")
} lines: { [Line], Configuration in
Text("My Custom Connection Lines")
} wrapper: { content in
content
}
Ideal Solution
At the moment, you have to do some pre-processing and cleanup, ideally, this would be a button in Xcode Previews that you could toggle - a one-click process
Limitations
Currently ImageRenderer fails to render NavigationView & TabView - possibly others, the workaround is to Generate an image of those views contents instead.
Ackowledgements
Thanks to Alasdair to alerting me to the European Accessibility Act 2025 Without you, I wouldn’t have been frustrated enough to make this tool.
About me
I’m Chris Davis, I love writing software. Feel free to contact me