●FUNDING — Rork raises a $15M seed led by Left Lane Capital●RORK MAX — Rork Max generates native Swift apps instead of React Native●PLATFORM — It targets iPhone, iPad, Watch, and Vision Pro, reaching Live Activities and Core ML●GROWTH — Traffic keeps climbing at 743K monthly visits and 85% growth●TEST — The Companion app lets you test on a real device without a paid Apple Developer account●STACK — Built on React Native and Expo for true native experiences, not web wrappers●FUNDING — Rork raises a $15M seed led by Left Lane Capital●RORK MAX — Rork Max generates native Swift apps instead of React Native●PLATFORM — It targets iPhone, iPad, Watch, and Vision Pro, reaching Live Activities and Core ML●GROWTH — Traffic keeps climbing at 743K monthly visits and 85% growth●TEST — The Companion app lets you test on a real device without a paid Apple Developer account●STACK — Built on React Native and Expo for true native experiences, not web wrappers
A hands-on walkthrough for tuning WidgetKit widgets for StandBy mode, the landscape charging display in iOS 17+. Covers always-on dimming, night mode, and container backgrounds, including which parts of Rork Max's generated code you still have to finish by hand.
Place a charging iPhone on its side at your bedside and the screen fills with a large clock and a row of widgets. That is StandBy, introduced in iOS 17. The other day I was rebuilding the widget for one of my own apps as an indie developer, "Law of Attraction Everyday," in Rork Max. It looked fine on the Home Screen, but the moment I dropped it into StandBy the text clipped, and under the dim red night display I couldn't read what it said at all.
StandBy is not simply a "show the widget bigger" mode. You are suddenly dealing with three different environments at once: dimming on the always-on display, the deep-night red mode, and a landscape-only layout. Rork Max builds the WidgetKit scaffolding from a plain-language prompt, but the part that bridges those three environments is something I had to read the generated code and finish myself. Here is what I learned, written up as an implementation sequence.
When StandBy activates, and what changes
As a starting point, StandBy only activates when all three of these are true at the same time.
The iPhone is charging (MagSafe, Lightning, or USB-C all qualify)
It is held in landscape orientation
It is locked
This combination is easy to overlook during development, and the simulator does not reproduce it reliably. I burned time trying to verify it in the simulator at first. It is safer to assume that the only dependable way to check StandBy behavior is to charge a real device in landscape.
StandBy widgets reuse the Home Screen's systemSmall family directly. Rather than adding a new widget family, your existing small widget lines up in the StandBy stack. What matters here is branching the presentation by environment values.
Environment
Environment value to read
What it needs
Normal Home Screen
default
Full color, normal information density
StandBy, daytime
widgetRenderingMode = fullColor
More padding, larger text
StandBy night mode
isLuminanceReduced = true
Red monochrome, minimal elements, less lit area
Always-on (14 Pro and later)
isLuminanceReduced = true
1Hz refresh assumed, animation suppressed
Night mode and the always-on display are both caught by isLuminanceReduced, but the intent differs slightly. The former is about not being glaring in a dark room; the latter is about limiting burn-in and power draw. In code, you can satisfy both by pushing in the same direction: reduce the lit area and stop motion.
Step 1: Always declare a container background
Since iOS 17, if a widget does not declare its background with containerBackground(for: .widget), you get cases in StandBy and on the Lock Screen where the background drops out and the text floats alone, or the layout collapses. In the code Rork Max generated, this declaration showed up as a hand-rolled background inside a ZStack, and that form broke in StandBy.
struct AffirmationWidgetView: View { var entry: AffirmationEntry var body: some View { VStack(alignment: .leading, spacing: 6) { Text(entry.affirmation) .font(.headline) .minimumScaleFactor(0.6) // allow shrink so it doesn't clip in StandBy .lineLimit(3) Spacer(minLength: 0) Text(entry.date, style: .time) .font(.caption2) .foregroundStyle(.secondary) } // ❌ A hand-rolled background alone peels off in StandBy // .background(Color.indigo) // ✅ Let the system recognize it as a container background .containerBackground(for: .widget) { Color.indigo } }}
I add minimumScaleFactor because in StandBy the effective drawing area of the same systemSmall expands, which makes a fixed font look relatively small. Leave the font size fixed and a long affirmation clips mid-sentence. This is a textbook case of something that causes no trouble on the Home Screen and only surfaces in StandBy.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦You'll be able to take a widget that looked broken in StandBy and make it hold up in both always-on and full-color states
✦You'll get working code for dimming-aware rendering using isLuminanceReduced and rendering modes
✦You'll learn exactly which parts of Rork Max's generated WidgetKit code you need to finish yourself
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Step 2: Respond to dimming (night mode and always-on)
When isLuminanceReduced is true, cut the area of white and bright colors as much as you can. In my case the app was meant to sit at the bedside at night, so getting this right shaped the experience more than anything else.
struct AffirmationWidgetView: View { @Environment(\.isLuminanceReduced) private var dimmed var entry: AffirmationEntry var body: some View { VStack(alignment: .leading, spacing: dimmed ? 4 : 6) { Text(entry.affirmation) .font(dimmed ? .subheadline : .headline) .foregroundStyle(dimmed ? .red.opacity(0.85) : .white) .lineLimit(dimmed ? 2 : 3) .minimumScaleFactor(0.6) if !dimmed { // When dimmed, drop secondary info to shrink the lit area Text(entry.date, style: .time) .font(.caption2) .foregroundStyle(.secondary) } } .containerBackground(for: .widget) { dimmed ? Color.black : Color.indigo } }}
The point is that when dimming, you don't just "darken the color," you "remove elements." The total count of lit pixels maps directly to perceived glare and to power, so cutting secondary text and decoration boldly makes the night experience better. At first I only turned the color red and kept the elements; placed at the bedside on a real device it was brighter than I expected, and I ended up settling on this decision to cut.
Step 3: Build around the always-on refresh rate
On an always-on iPhone, widget refresh is roughly capped at 1Hz, and it keeps drawing down the WidgetKit timeline refresh budget (around 40 to 70 reloads per day) as a separate allowance. A second-by-second countdown will not animate smoothly under always-on.
The realistic move here is to drop the "make it move" assumption and lean on system-managed displays like Text(date, style: .timer). If you design the timeline to emit an entry every minute, you can blow through the budget and have updates stop in the dead of night. Refresh-budget design is foundational to widgets in general, not just StandBy, so reading WidgetKit timeline refresh budget design alongside this will make your StandBy support a notch more stable.
Step 4: Interactive widgets work in StandBy too
Since iOS 17, interactive widgets built with App Intents (buttons and toggles) operate in StandBy as well. Adding a button to "advance to the next affirmation" was genuinely handy: I could switch it just by reaching over from the pillow.
struct NextAffirmationIntent: AppIntent { static var title: LocalizedStringResource = "Next Affirmation" func perform() async throws -> some IntentResult { AffirmationStore.shared.advance() return .result() }}// In the widgetButton(intent: NextAffirmationIntent()) { Image(systemName: "arrow.right.circle")}.buttonStyle(.plain)
That said, the tap target is hard to see when dimmed, so when isLuminanceReduced is true you have to decide whether to hide the button or leave only its outline. The App Intents design itself is covered in App Intents and Siri Shortcuts integration, which helps if you want to share the same entry point with Siri.
What Rork Max owns, and what you finish yourself
Let me lay out, honestly, the division of responsibility I felt through this build. Rork Max assembles the scaffolding — the WidgetKit Provider, Entry, and base view — from a natural-language instruction with fair accuracy. The systemSmall layout and even the App Intents skeleton were in a working state the moment they were generated.
On the other hand, the isLuminanceReduced branching that splits StandBy's three environments (day, night, always-on), the swap to containerBackground, and the timeline design that accounts for the refresh budget were not in the generated code. I see this less as a Rork Max weakness than as a consequence of StandBy being something you "only see once you charge a real device in landscape" — there is no way to detect it from a generation-time preview alone.
My operating rule settled into this split: let Rork Max handle the widget's skeleton, and always do one full pass on a real device before adding the StandBy-specific adjustments by hand. Even when using a generative tool, I've come to feel that these "environment differences a preview can't show" are something a person simply has to look at and finish — and accepting that is, in the end, the fastest path.
Area
Rork Max generation
Finish by hand
Provider / Entry / base view
Excellent, runs nearly as-is
Minor
containerBackground declaration
Often a hand-rolled background
Required
isLuminanceReduced branching
Not generated
Required
Budget-aware timeline
Tends to refresh every minute
Required
Interactive widget (App Intents)
Skeleton is accurate
Only dimmed-state display
Three things to check during verification
When I check StandBy on a real device, there are three things I always look at. First, whether the widget appears in the intended layout while charging in landscape and locked. Next, I darken the room and wait a few dozen seconds, then check whether the text is readable once it switches to night mode. Finally, on a 14 Pro or later, I leave it in always-on overnight and confirm the next morning that updates didn't stop in the dead of night.
That third one in particular surfaces, when your design exhausts the timeline budget, as "the time is frozen at 2 a.m." App Store reviewers tend to flag this, and it ties into the freshness of a widget's underlying data, so it's reassuring to read it together with the freshness handling covered in stale data via App Group in widgets.
Start with a single widget you already have, add just the containerBackground declaration and the isLuminanceReduced branch, and charge a real device in landscape. The breakage you couldn't catch on the Home Screen should reveal itself on the spot.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.