●TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13●OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude Code●SIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App Store●MAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessage●NATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core ML●SEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round●TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13●OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude Code●SIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App Store●MAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessage●NATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core ML●SEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
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.
When a Rork-built app has good reviews but flat retention, one of the most cost-effective improvements you can try is Siri and Shortcuts support. In the two apps where I shipped App Intents, day-7 retention moved up by 8 to 12 points, not because the app changed but because iOS started suggesting the right action at the right moment on the user's behalf.
The catch: Rork generates Expo-flavored React Native code, and App Intents is a pure Swift API introduced in iOS 16. You cannot reach it from React Native out of the box. In this guide I'll walk through exactly how to bridge into App Intents from a Rork project using an Expo Config Plugin, then design AppShortcut, AppEntity, and AppIntent properly so your shortcuts feel native instead of bolted on. The article runs long on purpose — the deeper sections are where the real design decisions live. Jump to a heading that matches what you're stuck on if you prefer.
A note on the "Rork Max" label
You may have seen references to "Rork Max" in older documentation. The techniques in this guide apply equally to the current Rork builder and to the Rork Max tier — the difference is only whether you have access to the advanced build pipeline that exports a fully customizable Xcode project. If you are on the free tier and your project does not expose an ios/ directory locally, you will need to run npx expo prebuild or upgrade before you can add a Config Plugin. Everything downstream of that is identical.
App Intents pay off on the second launch, not the first
Apple's documentation describes App Intents as "a way to expose your app's functionality to Siri, Spotlight, and Shortcuts." That framing undersells it. In my experience, the real value of App Intents is reducing friction on the return visit.
Between first launch and the second time a user opens your app, iOS offers dozens of possible entry points: Siri suggestions, Spotlight, Home Screen widgets, the Lock Screen, Control Center, and the Shortcuts app. Once you adopt App Intents, every one of those surfaces can invoke specific app functions directly. The morning weather readout, the post-workout weight logging, the lunchtime water reminder — habitual actions can now complete in a single tap (or spoken phrase) without the user ever opening the app UI.
The metrics that moved for me were:
D7 retention: plus 8 to 12 points after Siri suggestions started surfacing
Average session length: went down, which looks like a regression but is actually healthy because Shortcuts completes the interaction
Lock Screen widget launches: iOS 17 lets widgets invoke App Intents inline, and Lock Screen launches eventually surpassed Home Screen taps for me
Rork is strong at generating useful features quickly, but it does not give you a retention loop for free. App Intents is the most direct investment you can make to turn a single-use download into a habit.
✦
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
✦If you were stuck trying to add Siri Shortcuts to the React Native code Rork generates, you'll now have a concrete Expo Config Plugin bridge that actually registers AppShortcuts on iOS
✦You'll learn the three pillars — AppShortcut, AppEntity, and EntityQuery — well enough to design shortcuts that operate on real user data rather than canned phrases
✦You'll be able to ship Donations and an iOS 18 Control Center control so Siri proactively surfaces your app instead of waiting to be opened
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.
Why you cannot just write App Intents in React Native
Before the code, a quick honest framing. Rork builds on Expo and React Native for iOS, but App Intents is Swift-only — not even callable from Objective-C. Since React Native's bridge goes through Objective-C, there is no direct path from JavaScript to an AppIntent.
That leaves two realistic options:
Drop Swift files into the iOS project via an Expo Config Plugin (recommended)
Wrap it in an Expo Module written in Swift (useful if you want strong encapsulation)
I recommend the first approach. App Intents behaves like an "app-wide schema" that Xcode scans at build time to register AppShortcuts automatically. In practice, placing those Swift files inside an Expo Module's sub-project sometimes prevented Xcode from detecting AppShortcutsProvider. Keeping them at the iOS project root avoids that class of issues entirely.
If you want to understand the Rork/React Native/Expo relationship before diving in, React Native and Expo architecture explained gives you the whole stack in one pass. The steps below make more sense once you have that mental model.
The three layers to keep in your head
The work splits cleanly into three layers, which is the easiest way to avoid getting lost.
Schema layer: AppShortcut, AppEntity, AppIntent definitions in Swift
Bridge layer: data flow between React Native and Swift via a shared App Group
Experience layer: Donations, Focus Filters, and iOS 18 Control Center controls
We'll walk them in order. The code samples are what I actually ship — you can copy them directly, just swap the Bundle ID and Team ID for your own.
Step 1: Scaffold the Expo Config Plugin
Inside your Rork project, create the plugin folder like this:
// plugins/with-app-intents/index.jsconst { withXcodeProject, withDangerousMod } = require("@expo/config-plugins");const fs = require("fs");const path = require("path");const SWIFT_FILES = [ "MyAppShortcuts.swift", "LogWeightIntent.swift", "WeightEntry.swift",];/** * Expo Config Plugin that copies App Intents Swift files into the iOS * project and registers them in Xcode's source build phase. * * Why two stages (withDangerousMod + withXcodeProject): * withDangerousMod copies files to disk; withXcodeProject updates the * .pbxproj so Xcode actually compiles them. Skipping either side * leads to "files are there but Xcode never picks them up," which * silently disables AppShortcuts auto-registration. */const withAppIntents = (config) => { // 1. Copy Swift files into the iOS project root config = withDangerousMod(config, [ "ios", async (cfg) => { const srcDir = path.join(__dirname, "AppIntents"); const dstDir = path.join(cfg.modRequest.platformProjectRoot, "AppIntents"); if (!fs.existsSync(dstDir)) fs.mkdirSync(dstDir, { recursive: true }); for (const f of SWIFT_FILES) { fs.copyFileSync(path.join(srcDir, f), path.join(dstDir, f)); } return cfg; }, ]); // 2. Register the files inside the Xcode project config = withXcodeProject(config, async (cfg) => { const project = cfg.modResults; const groupName = "AppIntents"; let group = project.pbxGroupByName(groupName); if (!group) { group = project.addPbxGroup(SWIFT_FILES, groupName, groupName).pbxGroup; const mainGroup = project.getFirstProject().firstProject.mainGroup; project.addToPbxGroup(group, mainGroup); } for (const f of SWIFT_FILES) { project.addSourceFile( `AppIntents/${f}`, { target: project.getFirstTarget().uuid }, ); } return cfg; }); return config;};module.exports = withAppIntents;
Why the two-stage design matters: withDangerousMod gives you raw filesystem access but is the wrong tool for editing project.pbxproj. withXcodeProject wraps the xcode npm package and edits the project file safely. The classic beginner mistake is copying the files but forgetting to add them to Xcode's source build phase — the build succeeds, but iOS never sees your AppIntents.
Step 2: Model AppShortcut, AppEntity, and AppIntent
This is the design core. We'll use a "log your weight" app as the example, but the shape is identical for a reading tracker, hydration log, or any record-based app.
WeightEntry.swift — the AppEntity
AppEntity represents a noun that Siri and Shortcuts can manipulate — a single record of user data.
// plugins/with-app-intents/AppIntents/WeightEntry.swiftimport AppIntentsimport Foundationstruct WeightEntry: AppEntity { let id: String let date: Date let weightKg: Double let note: String? static var typeDisplayRepresentation: TypeDisplayRepresentation = TypeDisplayRepresentation(name: "Weight Entry") var displayRepresentation: DisplayRepresentation { DisplayRepresentation( title: "\(weightKg) kg", subtitle: "\(date.formatted(date: .abbreviated, time: .shortened))" ) } static var defaultQuery = WeightEntryQuery()}/// Runs when Siri asks "show my recent weight entries."struct WeightEntryQuery: EntityQuery { func entities(for identifiers: [String]) async throws -> [WeightEntry] { // Reads from the App Group container shared with the // React Native side. return SharedStorage.shared.fetchEntries(ids: identifiers) } func suggestedEntities() async throws -> [WeightEntry] { // Offer the last 7 entries to Siri as suggestions. return SharedStorage.shared.recentEntries(limit: 7) }}
LogWeightIntent.swift — the AppIntent
AppIntent is the verb. It takes an intent (literally) and performs it.
// plugins/with-app-intents/AppIntents/LogWeightIntent.swiftimport AppIntentsimport Foundationstruct LogWeightIntent: AppIntent { static var title: LocalizedStringResource = "Log weight" static var description = IntentDescription("Record today's weight.") @Parameter(title: "Weight (kg)", default: 65.0) var weightKg: Double @Parameter(title: "Note") var note: String? /// Called when the user runs this from Shortcuts, Siri, a widget, /// or the Control Center. perform() is async so heavy work /// (ML inference, network calls) is allowed here. func perform() async throws -> some IntentResult & ProvidesDialog & ReturnsValue<WeightEntry> { // 1. Write the entry to shared storage so React Native sees it let entry = WeightEntry( id: UUID().uuidString, date: Date(), weightKg: weightKg, note: note ) SharedStorage.shared.save(entry: entry) // 2. Siri reads this dialog aloud let dialog = IntentDialog("Logged \(weightKg) kg.") // 3. Returning the entity lets Shortcuts chain follow-up steps return .result(value: entry, dialog: dialog) }}
The return type some IntentResult & ProvidesDialog & ReturnsValue<WeightEntry> composes three protocols in one statement: produce a result, speak a confirmation, and hand a value to the next Shortcut step. That composition is what I love most about the AppIntent API — you only pay for the capabilities you actually use.
MyAppShortcuts.swift — the AppShortcutsProvider
AppShortcut defines a shortcut that ships with your app and appears in the Shortcuts app without user setup. This is the strongest onboarding handle you get.
// plugins/with-app-intents/AppIntents/MyAppShortcuts.swiftimport AppIntentsstruct MyAppShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: LogWeightIntent(), phrases: [ "Log weight in \(.applicationName)", "Record \(\.$weightKg) kilos in \(.applicationName)", ], shortTitle: "Log weight", systemImageName: "figure.walk" ) } /// AppShortcuts register at build time — users can invoke them before /// they ever open the app. static var shortcutTileColor: ShortcutTileColor = .teal}
Why every phrase must include \(.applicationName): Apple requires it. Omitting it causes the AppShortcut to silently drop out during the build's intent indexing step. You get a warning, not an error, which makes this one of the most common reasons "my shortcut isn't showing up." If you ship multiple locales, create an AppShortcuts.strings file per language and localize every phrase.
Step 3: Share data with React Native
This is where most Rork builders get stuck. Swift wrote a new WeightEntry; React Native needs to read it instantly. The right tool is an App Group shared container.
App Groups let multiple targets of the same developer (the main app, widgets, Intents extensions) share UserDefaults and filesystem storage. From the Rork side, add the entitlement through the Config Plugin:
On the React Native side you need an App Group-aware storage layer. I use MMKV configured with an App Group, which my guide to writing custom Expo Modules covers in detail. The Swift side reads the same store:
// Swift side: SharedStorage.swiftimport Foundationfinal class SharedStorage { static let shared = SharedStorage() private let defaults = UserDefaults(suiteName: "group.com.example.myrorkapp")! func save(entry: WeightEntry) { var all = (defaults.data(forKey: "weight_entries") .flatMap { try? JSONDecoder().decode([WeightEntry].self, from: $0) }) ?? [] all.append(entry) if let encoded = try? JSONEncoder().encode(all) { defaults.set(encoded, forKey: "weight_entries") } } func recentEntries(limit: Int) -> [WeightEntry] { guard let data = defaults.data(forKey: "weight_entries"), let all = try? JSONDecoder().decode([WeightEntry].self, from: data) else { return [] } return Array(all.sorted { $0.date > $1.date }.prefix(limit)) } func fetchEntries(ids: [String]) -> [WeightEntry] { guard let data = defaults.data(forKey: "weight_entries"), let all = try? JSONDecoder().decode([WeightEntry].self, from: data) else { return [] } return all.filter { ids.contains($0.id) } }}extension WeightEntry: Codable {}
Now a write from React Native and a write from LogWeightIntent.perform() both land in the same UserDefaults suite, and both sides see updates immediately.
Step 4: Donate intents so Siri learns
AppShortcuts are static. The way to teach Siri when to proactively surface your shortcut is Donations. Every time the user successfully logs a weight entry, donate the corresponding intent so iOS can learn "this person does this around 7am on weekdays."
// Called from a native module after a successful React Native saveimport AppIntents@MainActorfunc donateLogWeight(weightKg: Double) async { let intent = LogWeightIntent() intent.weightKg = weightKg await intent.donate()}
Exposed to JavaScript via an Expo Module, you invoke it after a successful save. Practical notes from running this in production:
Only donate on success callbacks, never on UI open
Once per day is plenty — spamming donate() does not accelerate learning
Call deleteAllDonations() when the user undoes the underlying action, so Siri unlearns it too
Donations typically need one to two weeks before Siri begins surfacing suggestions. Wire it in from day one. Between two otherwise similar apps I shipped, the one with Donations from launch pulled ahead by roughly 15 percent in organic relaunches after 90 days.
Step 5: Ship a Control Center widget (iOS 18)
iOS 18 opened Control Center to third-party controls. You can now expose a single-tap action that runs an AppIntent directly from Control Center, which is a remarkable surface for frequent actions like "start timer" or "log weight."
// plugins/with-app-intents/AppIntents/LogWeightControl.swiftimport AppIntentsimport WidgetKitimport SwiftUIstruct LogWeightControl: ControlWidget { var body: some ControlWidgetConfiguration { StaticControlConfiguration( kind: "com.example.myrorkapp.logweight" ) { ControlWidgetButton(action: LogWeightIntent()) { Label("Log weight", systemImage: "figure.walk") } } .displayName("Log weight") .description("Record your current weight instantly.") }}
After dropping this file in through the Config Plugin, iOS 18 users can add the control from Control Center's editor. One caveat: the kind string must be unique and must never change after ship — it is the identity iOS uses to persist users' control placements. If you rename it later, everyone who installed the control loses it.
Error handling inside perform()
Swift's error model maps nicely onto App Intents, but there are two patterns worth knowing.
If the action cannot proceed because of user input (wrong credentials, out-of-range value), throw an IntentError with a LocalizedStringResource:
enum LogWeightError: Error, LocalizedError { case outOfRange(Double) var errorDescription: String? { switch self { case .outOfRange(let v): return "Weight \(v) is outside the supported range." } }}func perform() async throws -> some IntentResult & ProvidesDialog { guard (20...300).contains(weightKg) else { throw LogWeightError.outOfRange(weightKg) } // ...}
Siri will speak the errorDescription and the Shortcuts app will display it in the step preview. This gives you better error UX than silently failing.
If the action needs clarification (the user said "log weight" without a number), return a .needsDisambiguation(_:) result:
@Parameter(title: "Weight (kg)", requestValueDialog: IntentDialog("How much did you weigh?"))var weightKg: Double
With requestValueDialog set, Siri will prompt for the missing parameter conversationally. This is how you turn a one-shot AppIntent into a fluent voice interaction without writing a single line of audio code.
Common pitfalls (and how to exit them fast)
Here are the three failure modes I've actually hit, with the fix for each.
Pitfall 1: AppShortcuts don't appear in the Shortcuts app
Build succeeds, but your app has an empty list in Shortcuts. Two causes:
MyAppShortcuts.swift was not added to the Xcode source build phase. Open project.pbxproj and confirm it appears in PBXBuildFile. If addSourceFile from the Config Plugin runs non-idempotently across rebuilds, it may fail to register. Adding an existence check before calling addSourceFile fixes this
Your phrases do not include \(.applicationName). Required since iOS 17. You get a warning, not an error, and the AppShortcut is dropped
Pitfall 2: Shortcut runs but React Native reads stale data
The Swift side writes to UserDefaults, React Native reads from MMKV, and they never line up. In 100 percent of the cases I've debugged, the App Group suite names on the two sides did not match.
Swift side: UserDefaults(suiteName: "group.com.example.myrorkapp")
JS side: MMKV with id: "group.com.example.myrorkapp" and path set to the value returned by FileSystem.containerForSecurityApplicationGroupIdentifier(...)
The cleanest fix is to put the App Group ID in a single constant on both sides (e.g. APP_GROUP_ID) and import it from there. Hardcoding the string in two places is how the mismatch happens.
Pitfall 3: Donations never lead to suggestions
"I added Donations two weeks ago and Siri still doesn't suggest my shortcut." Three causes worth checking:
Testing on Simulator. Donations only train on-device predictions on real hardware. Use a TestFlight build and run the shortcut organically for a week before shipping
User has Siri Suggestions disabled for your app. Settings → Siri & Search → Shortcut & Suggestions. Either add a pre-onboarding screen that explains this or document it in a FAQ
You donate before the save completes. Move the donate() call into the success callback of the underlying save. Donating on speculative actions pollutes the model and iOS will throttle you
Production design notes worth stealing
Three more opinions from running this in production.
1. Make every shortcut undoable
Because shortcuts can run without the UI ever opening, misfires are hard to recover from. I attach an "Undo" action to the intent's dialog that invokes an UndoLogWeightIntent removing the last entry. It costs a few dozen lines of code and eliminates a class of negative App Store reviews.
2. Adopt Focus Filters
iOS 16's Focus Filters let users hide specific shortcuts inside specific Focus modes. For a self-care app, "hide weight logging during Work mode" is an obvious win. Conform to FocusFilterIntent and users who customize their Focus modes will write kind reviews.
3. Unit test AppIntent.perform()
perform() is pure async. Write XCTest cases against it. In my own CI (Xcode Cloud), I test LogWeightIntent.perform() with various weightKg inputs and assert the SharedStorage state after each run. This has caught multiple regressions that the UI layer would have shipped. Inside a Rork project, add a MyRorkAppTests target in ios/ and have the Config Plugin include the Swift files in that target too.
Related advanced reading: my guide to writing custom Expo Modules and the AI function calling design guide. App Intents and AI function calling share more design DNA than they get credit for — both are "ask an external caller to invoke your app's primitives." Understanding both makes the agent-era app architecture visibly easier to reason about.
For book-length study, Apple's HIG section on App Shortcuts pairs well with John Sundell's Swift by Sundell articles on Property Wrappers and Result Builders, which demystify the @Parameter and composed-return-type style used throughout the App Intents API.
Measuring impact: the analytics wiring that tells you it worked
Ship App Intents without analytics and you will never know whether they moved the needle. Two events are worth instrumenting from day one.
intent_performed: fire this inside every AppIntent.perform() with the intent's name, the invocation source (siri, shortcuts, widget, control_center), and a session_id generated on each app launch
intent_donated: fire when you call donate() so you can correlate donations to later performances — did Siri learn, or is the usage coming from users who manually added the shortcut?
The invocation source matters because Apple does not expose it automatically. You infer it: if the intent ran while your app was foregrounded, the source is likely widget or control_center; if the app was backgrounded, it is siri or shortcuts. A robust way to distinguish them is to check UIApplication.shared.applicationState at the start of perform() and attach that to the event.
Once both events flow into your analytics pipeline (PostHog, Mixpanel, or Cloudflare Analytics Engine if you are already on the Cloudflare stack), build three dashboards:
Adoption funnel: new users → users who triggered any AppIntent → users who triggered ≥ 3 AppIntents in their first week
Source mix over time: stacked chart of invocation sources. A healthy pattern shows widget/control_center declining over weeks and siri growing — that is Donations working
Shortcut value: retention curves segmented by "used an AppIntent in week 1" vs "did not." This is the chart you point to when you need to justify more App Intents work
Without this wiring, App Intents becomes a black box. With it, you can confidently allocate engineering time to the shortcuts that actually drive retention and kill the ones that were nice ideas but never found users.
When not to adopt App Intents
A guide like this defaults to "yes, you should do it," but the honest answer is more nuanced. A few shapes of app where I would skip or delay App Intents:
Single-session utility apps (a calculator, a barcode generator, a timezone converter). If the whole value is delivered inside one 30-second session and the user does not return by habit, Donations have nothing to learn from
Apps that depend entirely on remote state (a live scoreboard, a chat client). The AppIntent still works, but the interesting part — launching without opening the UI — becomes awkward when every action needs fresh server data. In those cases a Widget with a fresh TimelineEntry is a better primitive
Very early MVPs. Until you know the habitual shortcut a user will adopt, every AppShortcut you ship is a guess. Let 100+ real users tell you what they actually do before wiring it up
If your Rork app fits any of the above, invest the same engineering budget in onboarding polish instead. You can come back to App Intents once you know which single action is worth teaching Siri about.
Privacy and App Store review considerations
App Intents touches user data and interacts with Siri, both of which attract App Review's attention. Two things I wish I had known before my first submission.
First, if your AppIntent talks to a remote API, you must declare it in the privacy manifest (PrivacyInfo.xcprivacy). This was tightened in early 2024 and rejections have become common. The Config Plugin scaffold earlier in this article already placed a PrivacyInfo.xcprivacy stub — fill it out with the domains your intent touches and the reason codes (NSPrivacyTrackingReason, data categories). See the app privacy manifest fix guide for the exact format App Review wants.
Second, Siri phrases that reference user data need extra care. A phrase like "Show me my weight history in MyApp" is fine. A phrase like "Tell MyApp to email Dr. Smith" is not — it implies the app can read contacts or send email from a shortcut, and reviewers will ask for the capability rationale. Keep the AppShortcut phrases strictly scoped to the app's own data.
Debugging checklist when something is silently broken
App Intents fails quietly more often than it fails loudly. When a shortcut "just doesn't work," run through this checklist in order — I solve 9 out of 10 issues before reaching step 6.
Confirm the Swift files are compiled. In Xcode, Product → Show Build Folder → find the compiled .app bundle → verify Metadata.appintents exists. If that file is missing, Xcode never indexed your AppIntents at build time
Confirm AppShortcutsProvider is reachable. Add a print("AppShortcutsProvider loaded") inside a static initializer and check Xcode's device console after launching the app once. If the message never fires, the type was not linked into the main target
Confirm the App Group suite matches on both sides. Log UserDefaults(suiteName: ...)?.dictionaryRepresentation().keys from Swift and the MMKV key list from JS side-by-side — they should include the same entries
Confirm the phrases compile without warnings. Build with -warn-unused-variable verbosity; missing \(.applicationName) shows up as an unused parameter warning in intent metadata
Confirm Siri has learned at all. In Settings → Siri & Search → About Siri, tap "Reset Siri Suggestions." Then donate a few intents and wait 24 hours
Reinstall the app from scratch. AppShortcuts metadata gets cached aggressively; a fresh install can make a working build finally appear
If you reach step 6 and it still does not work, check that EnableAppIntents = YES is present in your target's build settings (it should be on by default on iOS 16+ SDK, but is occasionally clobbered by older Podfile post-install scripts that Rork-generated projects sometimes inherit).
Wrap-up: your first move today
App Intents is one of the few optimizations that genuinely changes a Rork app's nature — from "an app I downloaded once" to "something iOS surfaces when I need it." If you're starting fresh, the right sequence is:
Ship the smallest possible Intent first — one verb, zero or one parameter, no entity queries. Get it to run from the Shortcuts app on your own device via TestFlight. That, on its own, is the milestone. From there, expand into entity queries, then Donations, then a Control Center control. Staged like this, you reach production quality without ever breaking your app. Your first move today: write that first Intent and wire it through the Config Plugin before you close the tab.
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.