Bakeshot
Capture the same UI your users see — without a simulator or a device.
Without Bakeshot
Getting the screens you want means installing the app, opening the right screen, getting it into the right state, placing the widget, configuring it, getting real data into it — and only then taking the shot.
| The screenshot you want | What you or your agent has to do first |
|---|---|
| A home screen with a month of history | Seed the database, or tap the data in by hand |
| A seven-day streak | Use the app for seven days running — or forge the dates in storage |
| The paid tier, active | Set up a StoreKit sandbox account and buy through it |
| An empty state, an error state | Wipe the data, or cut the network, and hope nothing else changes |
| A chart that looks good | Keep re-seeding until the numbers happen to make a nice shape |
| The same shots in five languages | Change the device language and take every shot again, five times |
| A widget | Install the app, add the widget, configure it, get data into it, then photograph the home screen and crop |
And you do all of it again next release.
With Bakeshot
You describe the state in code; Bakeshot builds your app, runs it on your Mac as a real iOS binary, and draws the view.
shotBoth("home") { HomeView().environmentObject(thirtyDaysOfHistory()) }
shot("paywall", dark: true) { PlusSheet().environmentObject(PlusStore(active: true)) }
Nothing is simulated or mocked up: these are your real views, your real fonts and assets, rendered by iOS. The app never has to reach the state — the state is handed to it.
It does not decorate. Backgrounds, captions and device frames are somebody else's job (try app-store-screenshots). Bakeshot does the part nobody has automated: the real UI, in the state that sells it.
Use it
pip install bakeshot
cd MyApp
bakeshot init
Then tell your coding agent:
Bakeshot を使って App Store Connect 用のプロモーション画像を作成して — or, in English, "use Bakeshot to create the promotional images for App Store Connect"
init installs a skill into .claude/skills/bakeshot/, so the agent reads your models and
views, writes the scene script, runs the render, fixes whatever crashed, and tells you where
the images are. You do not have to learn the flags, or the file, or any of this README.
Not using Claude Code?
Point any agent at the skill: "Read .claude/skills/bakeshot/SKILL.md and follow it."
Or drive it yourself: edit Bakeshot/Scenes.swift, then bakeshot bake --locale ja --locale en.
PNGs land in Bakeshot/out/<locale>/, at real store dimensions. Every language is
baked in a single Xcode run — the build happens once, not once per language.
The scene script
Bakeshot/Scenes.swift is where you say what to shoot and in what state. It is plain
Swift against your own types, which is why an agent can write it:
enum BakeshotScenes {
@MainActor static var scenes: [BakeshotScene] {
[
shotBoth("home_busy") { HomeView().environmentObject(busyStore()) },
shot("paywall", dark: true) { PlusSheet().environmentObject(PlusStore()) },
shotBoth("widget", size: .widgetMedium) { MyWidgetView(entry: demoEntry()) },
].flatMap { $0 }
}
}
@MainActor private func busyStore() -> AppStore {
let s = AppStore()
s.tags = [Tag(name: "Study"), Tag(name: "Workout"), Tag(name: "Work")]
s.logs = (0..<20).map { LogEntry(minutes: 30 + $0 * 5) }
return s
}
An empty app makes a worthless screenshot. The state is the product.
Does it touch my project?
No. Bakeshot never edits your files, your .xcodeproj, or your git history.
To render your views it needs one test target hosted by your app. Rather than add that
to your project, it works on a hidden throwaway copy of the .xcodeproj, and deletes it
when the run ends — including when the run fails. Nothing is left behind.
What actually happens
- Copy
MyApp.xcodeproj→.MyApp-bakeshot.xcodeproj(hidden, temporary, sits next to the original because Xcode projects reference their files by relative path). - Add a test target to the copy, hosted by your app.
- Build the copy for My Mac (Designed for iPad) — your iOS binary, running on macOS, so Firebase, UIKit, your fonts and assets are all the real thing.
- Render each scene with
UIHostingController+drawHierarchy, write PNGs. - Delete the copy.
Requirements
- Apple silicon Mac with Xcode
gem install --user-install xcodeproj- Your app must be signable with your own team — the setup you already build with
bakeshot init checks all of this and tells you what is missing.
Price
Free, all of it. Every screen, every state, every language, every device size, widgets included. MIT.
Known limits
- Xcode is driven while baking, so you cannot use Xcode during a run.
(
xcodebuild testcannot launch an iOS-on-Mac test host; the IDE can.) - A view crashes while rendering if something it demands is missing — nearly always an environment object, occasionally a force-unwrapped value that isn't there offline. Bakeshot reports the reason and carries on with the rest. Network calls do not crash a render; they simply don't finish, so the screen comes out in its loading state. Bakeshot names them and bakes the rest — give them fakes in the scene script.
- Widget sources are compiled into the test bundle, so a widget that depends on the extension's own asset catalog may render without those images.
Your data
The throwaway copy is built without the app's entitlements. That is deliberate: with them in place the copy opens the real App Group, and a scene script that happens to run a save or a sync path would write your live data — and, if the app syncs, push it to your other devices. Screenshots do not need the real store; the scene hands the views their state instead.
If an app truly cannot launch without its entitlements, --keep-entitlements puts them back.
Do that only when the scene script touches nothing that persists.
License
MIT
Metadata
Release files for bakeshot 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bakeshot-0.3.1.tar.gz | 39.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bakeshot-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 79.2 kB
Release files / bakeshot-0.3.1.tar.gz
| Download URL | bakeshot-0.3.1.tar.gz |
|---|---|
| Size | 39.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d71a4e72fde016892a33ec9a9e9e1d4719b9fccee7cf04273c75319399e2ae78
|
|
BLAKE2b-256 checksum How to use checksums |
4f666bbfc33a6099400d71668aafb9270f5dcc451be0377c8501601ddcf07c79
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / bakeshot-0.3.1-py3-none-any.whl
| Download URL | bakeshot-0.3.1-py3-none-any.whl |
|---|---|
| Size | 39.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c316a7a261baa351655708b73935648a16f10bb1be556e0a8bc9005565312250
|
|
BLAKE2b-256 checksum How to use checksums |
e3ce5014747c749cf55527132d4ae1592e06c9b5078c01a33ef7a6192c132634
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|