RORK LABJP
MAX — Rork Max builds native Swift apps instead of React Native, supporting iPhone, iPad, Watch, TV, Vision Pro, and iMessageNATIVE — It unlocks native capabilities: AR/LiDAR, Metal 3D games, Dynamic Island, Live Activities, HealthKit, and Core MLCORE — Standard Rork generates iOS/Android apps with React Native (Expo), taking you from plain English to the app storesFUNDING — Rork raised $2.8M from a16z (Andreessen Horowitz)GROWTH — The platform now sees 743,000 monthly visits with 85% growthPRICING — Free to start, with paid plans from $25/monthMAX — Rork Max builds native Swift apps instead of React Native, supporting iPhone, iPad, Watch, TV, Vision Pro, and iMessageNATIVE — It unlocks native capabilities: AR/LiDAR, Metal 3D games, Dynamic Island, Live Activities, HealthKit, and Core MLCORE — Standard Rork generates iOS/Android apps with React Native (Expo), taking you from plain English to the app storesFUNDING — Rork raised $2.8M from a16z (Andreessen Horowitz)GROWTH — The platform now sees 743,000 monthly visits with 85% growthPRICING — Free to start, with paid plans from $25/month
Articles/Dev Tools
Dev Tools/2026-06-23Advanced

The Private Screen That Lingers in the App Switcher — Hiding the Snapshot iOS Takes the Moment You Background Your App

When you send a React Native app generated by Rork to the background, iOS photographs the current screen for the App Switcher and writes it to disk. Journals and personal input screens linger there in plain sight. This walks through the iOS privacy overlay (why inactive, not background), Android's FLAG_SECURE, scoping it to sensitive screens only, and screenshot detection — all in working code.

Rork441React Native176Expo94Privacy5Security Design

Premium Article

One morning I opened my own journaling app, then reached to answer a call and double-tapped the home button. There, among the thumbnails in the App Switcher, was the personal line I'd been writing a moment earlier — fully legible. It wasn't anything I'd be mortified for family to see, but it hit me: this was a screen I never designed to be seen by anyone but me, and it had just been left out in the open.

The instant you send an app to the background, iOS photographs the current screen and uses it as the App Switcher preview. That image isn't held only in memory — it's written to disk. So the contents of your app sit on a home screen, before any passcode lock, for a while after you leave. Journals, manifestation notes, health data, the amount on a screen right before payment — the more a screen is meant for the owner's eyes only, the more it hurts to have it linger there.

I run several journaling and intention-tracking apps as an indie developer, and this defense of "the moment of leaving" isn't present by default in the React Native code Rork generates, nor in the Swift that Rork Max emits. This article builds the design that slips a shield in at exactly that moment — including the ordering pitfall that trips most people up first.

What you defend is the moment of leaving, not the time on screen

We tend to think of security as something that happens while a screen is displayed. But what we want to protect here is the split second of the transition as the user leaves. When your app drops out of the foreground, iOS takes a snapshot of the UI for the App Switcher. And it's captured not "after the app has fully gone to the background," but at the inactive state just before that.

React Native's AppState represents this transition with three values: active while running in the foreground, the transitional inactive that accepts no input (iOS only), and background. Pulling down Control Center, receiving a call, opening the App Switcher — all of these pass through inactive first.

Here's the first trap. If you show the shield only after the state becomes background, you miss the snapshot entirely. It was already taken back at inactive. So lock this in from the start: the entry point for your defense is inactive, not background.

iOS: show a privacy overlay on inactive

First, place an overlay at the root of the app that's transparent (effectively hidden) while active, and covers the whole screen otherwise. We keep state management lightweight — no external library, just a small AppState subscription hook.

// hooks/usePrivacyShield.ts
import { useEffect, useRef, useState } from "react";
import { AppState, AppStateStatus } from "react-native";
 
// Returns true whenever the state is NOT active (inactive / background).
// iOS captures its snapshot during inactive, so we must not wait for background.
export function usePrivacyShield(enabled: boolean) {
  const [shielded, setShielded] = useState(false);
  const appState = useRef<AppStateStatus>(AppState.currentState);
 
  useEffect(() => {
    if (!enabled) {
      setShielded(false);
      return;
    }
 
    const handle = (next: AppStateStatus) => {
      // Reveal only when we return to active.
      // Every other transition (inactive / background) falls to the covered side.
      setShielded(next !== "active");
      appState.current = next;
    };
 
    // Guard against the initial frame (launch can begin in inactive).
    setShielded(AppState.currentState !== "active");
 
    const sub = AppState.addEventListener("change", handle);
    return () => sub.remove();
  }, [enabled]);
 
  return shielded;
}

Use this hook in exactly one place at the very top of the app and lay the covering UI over everything. Don't put it per-screen: at the moment of transition there's no room to decide "which screen am I on right now," so it belongs at the root.

// components/PrivacyShield.tsx
import { View, StyleSheet, Image } from "react-native";
import { usePrivacyShield } from "../hooks/usePrivacyShield";
 
export function PrivacyShield({ enabled }: { enabled: boolean }) {
  const shielded = usePrivacyShield(enabled);
  if (!shielded) return null;
 
  // A single brand logo, centered. Draw none of the actual content.
  return (
    <View style={styles.cover} pointerEvents="none">
      <Image
        source={require("../assets/shield-logo.png")}
        style={{ width: 96, height: 96, opacity: 0.9 }}
        resizeMode="contain"
      />
    </View>
  );
}
 
const styles = StyleSheet.create({
  cover: {
    ...StyleSheet.absoluteFillObject,
    backgroundColor: "#0E1116",
    alignItems: "center",
    justifyContent: "center",
    zIndex: 9999,
  },
});

At the root, wire it up like this. Keeping enabled driven by "are we on a sensitive screen right now" lets you grow this into per-screen control later.

// app/_layout.tsx (Expo Router example)
import { Stack } from "expo-router";
import { PrivacyShield } from "../components/PrivacyShield";
 
export default function RootLayout() {
  return (
    <>
      <Stack />
      {/* Start by enabling it app-wide; scope it per-screen later */}
      <PrivacyShield enabled={true} />
    </>
  );
}

With this in place, the instant you return to the home screen or open the App Switcher, the screen flips to the single-logo shield, and the snapshot that gets captured is of the shielded state.

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've worried that a personal input screen stays visible in the App Switcher, you can ship an overlay that slips in at the exact moment of backgrounding — today
You'll understand why background is too late and inactive is the right trigger, and why iOS and Android defend this in completely different ways, so you never protect one platform and assume you're covered
You'll implement a production pattern that scopes FLAG_SECURE to sensitive screens only and uses screenshot detection to quietly inform the user
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 $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-06-22
When a Poisoned Cache Crashes Your App on Every Launch — Designing a Safe-Mode Boot Your Users Can Escape On Their Own
When a persisted cache goes bad and the app crashes at the same spot on every launch, the only option left to the user is to reinstall. This article designs a safe-mode boot for Expo (React Native): the app counts its own early crashes, confirms a launch only once it becomes interactive, and resets just the dangerous state in graduated steps.
Dev Tools2026-06-22
Build a Toast System in Your Rork App That Survives Overlaps, Screen Readers, and Notches
When you add toast notifications to a React Native app generated by Rork, the naive version breaks in three places: two toasts overlap, screen readers stay silent, and the text hides under the notch or home indicator. This walks through a root-level queue, animations decoupled from your app tree, AccessibilityInfo announcements, and safe-area placement — all in working code.
Dev Tools2026-06-21
Your Rork App's Photos Look Sideways Only After Upload — Normalizing EXIF Orientation and Stripping Location Metadata
A photo that looks upright in your app rotates 90 degrees once it hits your server. The cause is the EXIF Orientation tag. Here's how to bake the rotation into pixels and strip location metadata with expo-image-manipulator before uploading.
📚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
See all →