# App launch performance is within store limits

- **Pattern:** `ab-000494` (`app-store-review-blockers.completeness-stability.launch-performance`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000494
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000494)

## Why it matters

Apple's watchdog process kills any app that doesn't become interactive within 20 seconds of launch. Google Play triggers an ANR (Application Not Responding) rejection when the main thread blocks for more than 5 seconds. Synchronous I/O in module initialization, blocking API calls before the first render, or missing `SplashScreen.hideAsync()` calls all cause the app to appear frozen — ISO 25010:2011 time-behaviour failure. A slow launch doesn't just risk rejection; it trains reviewers to view the app as low quality, increasing scrutiny of everything else.

## Severity rationale

High because exceeding Apple's 20-second watchdog threshold causes an automatic process kill and rejection, reproducible on any network-constrained device.

## Remediation

Move all async startup work behind the splash screen and defer it past first render.

```tsx
// app/_layout.tsx (Expo Router)
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';

SplashScreen.preventAutoHideAsync();

export default function RootLayout() {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    async function prepare() {
      try {
        await Font.loadAsync({ 'MyFont': require('./assets/MyFont.ttf') });
      } finally {
        setReady(true);
        await SplashScreen.hideAsync();
      }
    }
    prepare();
  }, []);

  if (!ready) return null;
  return <Slot />;
}
```

Add a 10-second `AbortController` timeout to any API call that runs during startup. Test on a low-end device or throttled simulator, not a flagship.

## Detection

- **ID:** `launch-performance`
- **Severity:** `high`
- **What to look for:** Count all relevant instances and enumerate each. Look for patterns that delay app launch: large synchronous operations in module-level code executed before the first render; blocking API calls in the root component's `useEffect` with no loading state (app appears frozen until data loads); `setTimeout` or `setInterval` calls at startup that delay rendering; heavy image loading without lazy/progressive strategies; use of `expo-font` or `expo-asset` without `SplashScreen.preventAutoHideAsync()` + `SplashScreen.hideAsync()` (causing either a flash of unstyled content or an extended black screen). For Android, look for `Application.onCreate()` doing heavy work. For iOS, look for `application(_:didFinishLaunchingWithOptions:)` doing synchronous I/O. Also check `app.json` for any explicit delay in the splash screen config.
- **Pass criteria:** App startup path is non-blocking. At least 1 implementation must be verified. Heavy initialization is deferred until after the first render. Splash screen shows during loading and dismisses when the app is ready.
- **Fail criteria:** Synchronous blocking operations at launch; API calls with no timeout that could make the app unresponsive for >20 seconds (Apple's watchdog threshold); no loading state for async startup dependencies.
- **Skip (N/A) when:** Never.
- **Detail on fail:** `"Root component fetches user profile synchronously before first render — app will appear frozen on slow networks"` or `"SplashScreen.hideAsync() never called — splash screen may persist indefinitely"`
- **Remediation:** Apple's watchdog kills apps that don't become responsive within 20 seconds. Google Play rejects apps that trigger ANR (Application Not Responding) dialogs.
  1. Move all async startup work into async `useEffect` hooks with loading states
  2. For font loading with Expo:
     ```tsx
     await SplashScreen.preventAutoHideAsync();
     await Font.loadAsync({ ... });
     await SplashScreen.hideAsync();
     ```
  3. Add timeouts to any startup API calls — don't let them block indefinitely
  4. Test startup time on a low-end device (not just a flagship simulator)

## External references

- iso-25010 time-behaviour
- external apple-watchdog-launch — https://developer.apple.com/documentation/xcode/understanding-the-crashes-on-ios

Taxons: performance, regulatory-conformance

HTML version: https://auditbuffet.com/patterns/ab-000494
