●MAX — Rork Max bills itself as the first web Swift app builder, publishing to the App Store in two clicks with no Xcode required●APPLE — It generates native Swift apps for iPhone, iPad, Apple Watch, Apple TV, and Vision Pro●EXPO — The standard tier builds native iOS and Android apps on React Native (Expo) from a plain-English description●FUNDING — Rork raised $2.8M from a16z, strengthening its position in AI no-code mobile development●PRICE — Free to start, with paid plans from $25/month — an accessible entry point for solo developers●WWDC — WWDC 2026 pushes Apple Intelligence forward, raising the value of native features and widening AI integration options for no-code apps●MAX — Rork Max bills itself as the first web Swift app builder, publishing to the App Store in two clicks with no Xcode required●APPLE — It generates native Swift apps for iPhone, iPad, Apple Watch, Apple TV, and Vision Pro●EXPO — The standard tier builds native iOS and Android apps on React Native (Expo) from a plain-English description●FUNDING — Rork raised $2.8M from a16z, strengthening its position in AI no-code mobile development●PRICE — Free to start, with paid plans from $25/month — an accessible entry point for solo developers●WWDC — WWDC 2026 pushes Apple Intelligence forward, raising the value of native features and widening AI integration options for no-code apps
Win Back Lapsed Users with App Store In-App Events — Deep Link Implementation for Rork (Expo) Apps
An implementation memo on bringing lapsed users back to a Rork (Expo) app using App Store in-app events. Covers event card design, universal link routing, and measurement, from a solo developer's operational view.
Running six apps as a solo developer, the biggest single block I see every month is the lapsed cohort: people who installed an app but haven't opened it in over 30 days. Push notifications can't reach the ones who turned permissions off.
The one surface where I can still create a touchpoint with that cohort is the App Store product page. When they rediscover the app through search or a chart, I can show a small event card on the product page — that mechanism is In-App Events.
I tried this on one of my wallpaper apps by turning a seasonal content drop into an event, and it added one more path back from the product page. Here I'll share how to wire App Store in-app events into a Rork (Expo) app and route lapsed users all the way to a destination inside the app.
Why In-App Events Are a Re-Engagement Path Without Ad Spend
In-app events are Apple's official way to surface an event card on your App Store product page, in search results, and in editorial tabs. Without spending on ads, you can tell people who already know your app that "this is happening right now."
The key is that tapping the card lands the user directly on the relevant screen. It isn't just a launch — it carries them straight into the event's substance (a new season, a live stream, a limited-time challenge). That single straight path reduces the all-too-common "opened it, didn't know what to do, closed it" outcome for lapsed users.
In App Store Connect you can keep up to 10 approved events per app at the same time. In my operation, I run a baseline of two slots — one always-on monthly event and one limited-time spot event — and keep the rest open as a buffer for scheduled drops.
Decide the Type and Target First
Before touching code, lock down the event design. Leaving this vague is what gets your card bounced in review later.
Apple offers seven event purposes:
Challenge (reach a goal within a window)
Competition (users compete against each other)
Live Event (happens at a set time)
Major Update (a large feature addition)
New Season (a new chapter of recurring content)
Premiere (the first release of new content)
Special Event (anything that doesn't fit the above)
For a solo developer's calm-themed or wallpaper apps, "New Season" or "Premiere" fit naturally. Challenge and Competition are for apps with social mechanics; forcing them on makes your intent harder for the reviewer to read.
You can target three segments: new users, lapsed users who installed but haven't opened recently, and currently active users. You can also broadcast to everyone, but I split the card copy between lapsed and new. For the lapsed cohort I write "a reason to come back"; for new users, "a reason to start now."
The event name caps at 30 characters and the short description at 50. That tight limit is worth real editing time. Something concrete like "June's new wallpapers are live" — telling people exactly what waits on the other side — performs best.
✦
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
✦How to choose among the 7 event types and the new / lapsed / active audience segments
✦Routing a universal link to the right expo-router screen instead of dumping users on the home tab
✦Building a 3-stage funnel from card impression to open to return action
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.
Every in-app event requires a deep link per card. For security, Apple recommends Universal Links. Avoid URL shorteners and extra redirects, because your app can't resolve them and the tap drops onto the home screen instead.
To receive universal links in a Rork (Expo) app, first declare the domain association in app.json.
This file must be served at https://events.example.com/.well-known/apple-app-site-association, with Content-Type: application/json, and no file extension. I tripped here once: a Cloudflare rule failed to attach the JSON content type to the extension-less path, so the link went silent and only launched the app. Always verify the response headers on a real device.
Build the landing handler in expo-router. Define a /event/[id] route and assemble the intended screen state based on the event ID.
// app/event/[id].tsximport { useLocalSearchParams, useRouter } from "expo-router";import { useEffect } from "react";import { logEvent } from "../../lib/analytics";const EVENT_DESTINATIONS: Record<string, string> = { "june-new-wallpapers": "/collection/2026-06", "summer-premiere": "/collection/summer",};export default function InAppEventEntry() { const { id } = useLocalSearchParams<{ id: string }>(); const router = useRouter(); useEffect(() => { if (!id) return; // Always record which event card brought the user in logEvent("iae_open", { event_id: id }); const destination = EVENT_DESTINATIONS[id] ?? "/"; // Replace rather than push so /event/[id] doesn't linger in history router.replace(destination); }, [id]); return null;}
The crucial choice is router.replace instead of router.push. With push, the back button returns to the /event/[id] waypoint and confuses the user. Treat the event landing as a transit point, not a place to stay.
Don't Drop the Launch Path
Universal links arrive via two paths: a cold start when the app isn't running, and a warm start resuming from the background. expo-router routes foreground links automatically, but it's safer to explicitly grab the initial URL on cold start.
// lib/useInitialEventLink.tsimport * as Linking from "expo-linking";import { useEffect } from "react";import { useRouter } from "expo-router";export function useInitialEventLink() { const router = useRouter(); useEffect(() => { let handled = false; Linking.getInitialURL().then((url) => { if (!url || handled) return; const { path } = Linking.parse(url); if (path?.startsWith("event/")) { handled = true; router.replace("/" + path); } }); }, []);}
Test on a real device by pasting https://events.example.com/event/june-new-wallpapers into the Notes app and tapping it. The simulator is unreliable for universal links, so always test on hardware, and try both a fully terminated app and a backgrounded one.
Measure Impression Through Return in Three Stages
So the event isn't a fire-and-forget, design the measurement up front. The effect of an in-app event is easiest to judge through this three-stage funnel.
Impressions — how often the card was displayed (from App Store Connect analytics)
Opens — how often a tap launched the app (the iae_open event)
Return actions — how often the landing led to the intended behavior (saving a wallpaper, browsing a collection)
Stage one comes from App Store Connect's Analytics as in-app event impressions. The rest you fill in with your own analytics. I attach a dedicated tag on the landing screen to distinguish event-driven sessions.
// lib/analytics.tslet activeEventId: string | null = null;export function logEvent(name: string, params: Record<string, unknown> = {}) { // Swap the actual sink for Firebase Analytics, etc. if (name === "iae_open" && typeof params.event_id === "string") { activeEventId = params.event_id; } send(name, { ...params, via_event: activeEventId });}export function clearEventAttribution() { activeEventId = null;}
Stamping via_event onto every event lets you trace, after the fact, "for users who arrived through the event, how far into the app did they get." In my operation I make the open-to-return-action rate the primary metric; events that don't move it get their copy or card image swapped the following month.
The numbers in App Store Connect and inside the app won't match exactly, because the measurement timing differs. Treat impressions as Apple's side and opens onward as your side, and watch each trend separately — that keeps the operation sane.
Pitfalls in Review and Publish Timing
The event card goes through App Review separately from the app binary. The easy thing to miss here is lead time. Submit an event right before your intended go-live date and review may not finish, leaving the card invisible when the start date arrives. I submit at least five business days before the start date.
The most common rejection is a mismatch between the card copy and the landing content. If the card says "new release live" but the landing is still a generic home screen, the reviewer reads it as a promise that doesn't match reality. Building the deep link destination to match the event's actual substance helps both approval and effectiveness.
One more thing: events have a start and end period, so make sure the deep link doesn't land on a dead screen after the event ends. I fall back any ID not present in EVENT_DESTINATIONS to the home screen. Even if an old link gets tapped after limited-time content is removed, no one sees a blank screen.
Your Next Step
Start by preparing a single "New Season" or "Premiere" event on an app you already run, and ship it small, targeted at the lapsed segment. Build out one clean universal-link destination, measure the open-to-return-action rate for a month, and the number will tell you what to swap next.
I hope it makes a useful first step for fellow solo developers wrestling with winning lapsed users back.
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.