Part 2: The 7-Day Launch Roadmap

A realistic day-by-day plan to go from idea to App Store.

Before You Start

Prerequisites Checklist

  • Apple Developer Account ($99/year)
  • Google Play Developer Account ($25 one-time)
  • Node.js 18+ installed
  • Expo account (free)
  • Convex account (free)
Pro Tip: Apple review can take 24-48 hours. Submit early, iterate later.

Day 1: Foundation

Morning (2-3 hours)

1. Project Setup

npx create-expo-app@latest my-app
cd my-app
npx expo install expo-router

2. Configure Expo Router

  • Create app/ directory
  • Set up basic navigation structure
  • Add index.tsx and _layout.tsx

3. Set Up Convex

.env
CONVEX_URL=https://xxxxx.convex.co
CONVEX_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
lib/convex.ts
import { ConvexReactClient } from 'convex/react';
import AsyncStorage from '@react-native-async-storage/async-storage';

export const convex = createClient(
  process.env.CONVEX_URL!,
  process.env.CONVEX_AUTH_TOKEN!,
  {
    auth: {
      storage: AsyncStorage,
      autoRefreshToken: true,
      persistSession: true,
      detectSessionInUrl: false,
    },
  }
);

Afternoon (2-3 hours)

4. Design System Foundation

constants/theme.ts
export const colors = {
  light: {
    primary: '#7C3AED',
    background: '#FFFFFF',
    surface: '#F9FAFB',
    text: '#111827',
    textSecondary: '#6B7280',
    border: '#E5E7EB',
    error: '#EF4444',
    success: '#22C55E',
  },
  dark: {
    primary: '#A78BFA',
    background: '#111827',
    surface: '#1F2937',
    text: '#F9FAFB',
    textSecondary: '#9CA3AF',
    border: '#374151',
    error: '#F87171',
    success: '#4ADE80',
  },
};

export const spacing = {
  xs: 4,
  sm: 8,
  md: 16,
  lg: 24,
  xl: 32,
};
components/ui/Button.tsx
import { TouchableOpacity, Text, StyleSheet, ActivityIndicator } from 'react-native';

interface ButtonProps {
  title: string;
  onPress: () => void;
  variant?: 'primary' | 'secondary' | 'outline';
  loading?: boolean;
  disabled?: boolean;
}

export function Button({
  title,
  onPress,
  variant = 'primary',
  loading,
  disabled
}: ButtonProps) {
  return (
    <TouchableOpacity
      style={[styles.base, styles[variant], disabled && styles.disabled]}
      onPress={onPress}
      disabled={disabled || loading}
    >
      {loading ? (
        <ActivityIndicator color="#fff" />
      ) : (
        <Text style={[styles.text, styles[`${variant}Text`]]}>{title}</Text>
      )}
    </TouchableOpacity>
  );
}
Day 1 Deliverable: Running app with navigation and basic theming

Day 2: Authentication

Morning (2-3 hours)

  • Enable Email auth in Convex dashboard
  • Configure OAuth providers (Google, Apple)
  • Set up redirect URLs

Auth Store

stores/useAuthStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { convex } from '@/lib/convex';

interface AuthState {
  user: User | null;
  session: Session | null;
  isLoading: boolean;
  signIn: (email: string, password: string) => Promise<void>;
  signUp: (email: string, password: string) => Promise<void>;
  signOut: () => Promise<void>;
  initialize: () => Promise<void>;
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      user: null,
      session: null,
      isLoading: true,

      initialize: async () => {
        const { data: { session } } = await convex.auth.getSession();
        set({
          session,
          user: session?.user ?? null,
          isLoading: false,
        });

        convex.auth.onAuthStateChange((_event, session) => {
          set({ session, user: session?.user ?? null });
        });
      },

      signIn: async (email, password) => {
        const { error } = await convex.auth.signInWithPassword({
          email,
          password,
        });
        if (error) throw error;
      },

      signUp: async (email, password) => {
        const { error } = await convex.auth.signUp({
          email,
          password,
        });
        if (error) throw error;
      },

      signOut: async () => {
        await convex.auth.signOut();
        set({ user: null, session: null });
      },
    }),
    {
      name: 'auth-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);
Day 2 Deliverable: Users can sign up, log in, and log out

Day 3: Core Feature

This is YOUR app's unique feature. Examples:

  • AI image generation
  • Habit tracking
  • Expense logging
  • Recipe saving

Data Model Example

SQL
-- Example: AI generations table
CREATE TABLE generations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES auth.users(id),
  prompt TEXT NOT NULL,
  result_url TEXT,
  created_at TIMESTAMP DEFAULT NOW()
);

-- Enable document-level access control
ALTER TABLE generations ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can view own data"
ON generations FOR SELECT
USING (auth.uid() = user_id);
Day 3 Deliverable: Core feature is functional and testable

Day 4: Monetization

npx expo install react-native-purchases
app/paywall.tsx
import { useState, useEffect } from 'react';
import { View, Text, Alert, ScrollView } from 'react-native';
import Purchases, { PurchasesPackage } from 'react-native-purchases';
import { router } from 'expo-router';
import { Button } from '@/components/ui/Button';

export default function PaywallScreen() {
  const [packages, setPackages] = useState<PurchasesPackage[]>([]);
  const [selectedPackage, setSelectedPackage] = useState<PurchasesPackage | null>(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    loadOfferings();
  }, []);

  const loadOfferings = async () => {
    const offerings = await Purchases.getOfferings();
    if (offerings.current?.availablePackages) {
      setPackages(offerings.current.availablePackages);
      const yearly = offerings.current.availablePackages.find(
        p => p.packageType === 'ANNUAL'
      );
      setSelectedPackage(yearly || offerings.current.availablePackages[0]);
    }
  };

  const handlePurchase = async () => {
    if (!selectedPackage) return;
    try {
      setLoading(true);
      const { customerInfo } = await Purchases.purchasePackage(selectedPackage);
      if (customerInfo.entitlements.active['pro']) {
        Alert.alert('Success!', 'Welcome to Pro!');
        router.back();
      }
    } catch (error) {
      if (!error.userCancelled) {
        Alert.alert('Error', error.message);
      }
    } finally {
      setLoading(false);
    }
  };

  return (
    <ScrollView>
      <Text>Upgrade to Pro</Text>
      {/* Package selection UI */}
      <Button
        title={`Subscribe ${selectedPackage?.product.priceString || ''}`}
        onPress={handlePurchase}
        loading={loading}
      />
    </ScrollView>
  );
}
Day 4 Deliverable: Users can upgrade to premium

Day 5: Polish & Settings

  • Settings Screen (theme, notifications, language)
  • Profile Screen (user info, usage stats)
  • Onboarding Flow (3-4 screens)
  • Error Handling (Sentry setup)
  • Performance Optimization (image caching, lazy loading)
  • Haptic Feedback
Day 5 Deliverable: App feels complete and polished

Day 6: App Store Assets

Screenshots

DeviceSizeRequired
iPhone 6.7"1290 x 2796Yes
iPhone 6.5"1284 x 2778Yes
iPad Pro 12.9"2048 x 2732If iPad support
Day 6 Deliverable: All marketing assets ready

Day 7: Submit & Launch

Build & Submit
# Install EAS CLI
npm install -g eas-cli

# Configure
eas build:configure

# Build for both platforms
eas build --platform all --profile production

# Submit to stores
eas submit --platform ios
eas submit --platform android
Day 7 Deliverable: App submitted to both stores!

Time Investment

DayFocusHours
1Foundation5-6
2Authentication5-6
3Core Feature6-8
4Monetization5-6
5Polish4-5
6Assets5-6
7Submit4-5
Total35-42
Ship in Hours, Not Days

Or launch today with everything built

Why spend 7 days building when you can start with production-ready code?

Day 1-7 already done
Auth & payments ready
Just add your features
Get ShipReactNative
Save 40+ hours of setup