- Overview
- Quick Start
- Configuration
- Continuous Design Review
- I don't like the design, can I change it?
- Ideal Solution
- Limitations
- Ackowledgements
- About me
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) |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
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.
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.
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 to your Xcode Project or Swift Package
.package(url: "https://github.com/nthState/DesignReviewToolkit", exact: "0.0.1")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)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)
}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
If you want a particular file to be skipped and not pre-processed, you can add this comment in your file.
// designreviewtoolkit:skipYou 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
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"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
}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
Currently ImageRenderer fails to render NavigationView & TabView - possibly others, the workaround is to
Generate an image of those views contents instead.
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.
I'm Chris Davis, I love writing software. Feel free to contact me





