◉RORK LABJP
●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●RORK — GPT-6.1 Sol (Sep 29) and Claude Sonnet 5.5 (Sep 28) are now in the model menu●RN 0.88 — React Native 0.88.x is scheduled for Oct 12, 2 days left●Q&A — People keep asking why AuthSession returns no result in Android standalone builds●WINDOWS — A walkthrough shows how to ship an iPhone app from Windows only, with no Mac●NEW — When only notifications fail in Expo Go: three checks to run first●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●RORK — GPT-6.1 Sol (Sep 29) and Claude Sonnet 5.5 (Sep 28) are now in the model menu●RN 0.88 — React Native 0.88.x is scheduled for Oct 12, 2 days left●Q&A — People keep asking why AuthSession returns no result in Android standalone builds●WINDOWS — A walkthrough shows how to ship an iPhone app from Windows only, with no Mac●NEW — When only notifications fail in Expo Go: three checks to run first
Articles/Dev Tools
⬡ Dev Tools/2026-06-03Advanced

Putting a Wallpaper Shuffle in Control Center — a ControlWidget in a Rork (Expo) App, Rebuilt for iOS 26

Field notes on bolting a Control Center control (ControlWidget) onto a Rork-built React Native wallpaper app — App Group sharing, AppIntent, the Expo config plugin that survives prebuild, and what changed when I rebuilt it for iOS 26 accented rendering.

iOS 265ControlWidgetApp Intents6Expo213wallpaper app22

✦ Premium Article

"Let me change today's wallpaper without unlocking the phone." That request started trickling into the reviews of one of my wallpaper apps (Beautiful Wallpapers) right after iOS 18 shipped. The trouble with wallpaper and calm-utility apps is that opening the app is a surprisingly heavy step, and on the days nobody opens it, no ads are served either.

iOS 18 finally let third-party apps place a control in Control Center, so a single button pulled down from the corner can run logic without launching the app. For a wallpaper app, that is one of the few legitimate ways to claw back the "never opened today" days.

I retrofitted a ControlWidget onto a React Native (Expo) app generated with Rork back then, and recently rebuilt it for the iOS 26 generation. These are the original notes with my current judgment layered on top.

Why a ControlWidget fits a wallpaper app

Custom controls are built as part of WidgetKit. They are cousins of home-screen widgets: you create a small extension target conforming to the ControlWidget protocol, and users can then add your control through Settings or by customizing Control Center.

There are two shapes — ControlWidgetButton and ControlWidgetToggle. Wallpaper shuffle wants the button: tapping it fires an AppIntent that picks the next wallpaper and writes the index to UserDefaults. Because AdMob revenue tracks impressions, adding a lighter touch point in front of the heavy "launch the app" path matters. Even a few percent more sessions per month adds up at the thin per-unit economics of a utility app.

One honest caveat: the control cannot actually set the wallpaper. iOS does not let third-party apps change the lock or home screen wallpaper directly. So I split the design cleanly: the control decides "today's next image," and the user finishes the actual set through a Shortcut or the share sheet. The control owns the decision, not the act.

Architecture — the App Group is the spine

The body of a Rork (Expo) app runs in the JavaScript layer, but a ControlWidget runs as a separate native extension process. They cannot share memory directly, so UserDefaults backed by an App Group becomes the spine.

Three pieces:

  1. The main app (Expo / React Native) writes the wallpaper index into the App Group UserDefaults.
  2. The control extension (Swift / WidgetKit) holds the ControlWidget and AppIntent.
  3. The App Group (an identifier like group.design.dolice.wallpaper) is the shared store.

Built in that order, the data flow stays one-directional: JS writes "total count" and "current position," and the AppIntent reads them to advance the index.

Pull the identifiers into constants. That was the very first thing I did during the rebuild.

enum WallpaperControlKind {
    static let appGroup = "group.design.dolice.wallpaper"
    static let shuffle = "design.dolice.wallpaper.shuffle"
    static let dailyShuffle = "design.dolice.wallpaper.dailyShuffle"
}
✦

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
✦A working ControlWidget + AppIntent that shuffles the wallpaper from Control Center in one tap, with the kind constant factored out
✦The three-part recipe for stateful controls — ControlWidgetToggle, ControlValueProvider, SetValueIntent — and a table for choosing between them
✦An App Group spine for sharing state between the Rork (Expo) JS layer and the native extension, plus a config plugin that survives every prebuild
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.

or
Unlock all articles with Membership →
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.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $15 for lifetime access
View Membership →

Related Articles

⬡ Dev Tools2026-04-23
Integrating Siri and Shortcuts into Rork Apps: App Intents with Expo, Without the Pitfalls
A production-grade guide to adding Siri Shortcuts and App Intents to a React Native app generated by Rork. Bridge native Swift via an Expo Config Plugin, model AppShortcut / AppEntity / EntityQuery correctly, and ship Donations and Control Center widgets without breaking Apple's conventions.
⬡ Dev Tools2026-10-07
Japanese Headings Won't Turn Bold on Android: How to Trace and Fix a fontWeight That Does Nothing
A troubleshooting note for Rork apps where headings look bold on iPhone but stay thin on Android. How to tell the symptoms apart, pick weights through fontFamily, and verify the fix.
⬡ Dev Tools2026-09-22
Four Numbers to Record Before R8 Becomes the Default — Android Measurements While We Wait for Expo SDK 58
Expo SDK 58 turns on R8 by default for Android release builds. Here is how I capture four numbers — download size, DEX total, cold start, and build time — so the before and after can be compared with the same ruler.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links