951 lines
21 KiB
Markdown
951 lines
21 KiB
Markdown
# Mobile Mode - Expo + React Native
|
|
|
|
## Mode Overview
|
|
|
|
**Purpose**: Cross-platform mobile applications with production-ready tooling
|
|
|
|
**Target Users**:
|
|
- Building iOS and Android apps from single codebase
|
|
- Mobile-first products and services
|
|
- Teams wanting native performance with React
|
|
- Startups needing fast mobile development
|
|
- Enterprise mobile applications
|
|
|
|
**Setup Time**: ~60 seconds (after npm install)
|
|
|
|
**Philosophy**: Native performance, React developer experience, production standards from day one.
|
|
|
|
---
|
|
|
|
## Tech Stack
|
|
|
|
```yaml
|
|
Framework:
|
|
- Expo SDK 50+ (managed workflow)
|
|
- React Native (latest stable)
|
|
- TypeScript 5+ (strict mode)
|
|
|
|
Navigation:
|
|
- Expo Router (file-based routing)
|
|
- React Navigation (under the hood)
|
|
|
|
Testing:
|
|
- Jest (React Native default)
|
|
- React Native Testing Library
|
|
- 80% coverage threshold
|
|
|
|
Code Quality:
|
|
- ESLint (React Native rules)
|
|
- Prettier (consistent formatting)
|
|
- Husky (pre-commit hooks)
|
|
- lint-staged (staged files only)
|
|
|
|
Build & Deploy:
|
|
- EAS Build (cloud builds - optional)
|
|
- EAS Submit (app store submission - optional)
|
|
- OTA Updates (instant updates)
|
|
|
|
Standards:
|
|
- Conventional Commits
|
|
- TypeScript strict mode
|
|
- Testing Trophy approach
|
|
```
|
|
|
|
---
|
|
|
|
## Workflow
|
|
|
|
### Step 1: Validate Prerequisites
|
|
|
|
```bash
|
|
# Check Node.js version (>= 18)
|
|
node --version
|
|
|
|
# Check npm version (>= 9)
|
|
npm --version
|
|
|
|
# Check git is installed
|
|
git --version
|
|
|
|
# Check if Expo CLI is needed (will be installed via npx)
|
|
echo "Expo CLI will be used via npx"
|
|
```
|
|
|
|
**If validation fails**: Provide upgrade instructions
|
|
|
|
### Step 2: Ask Configuration Questions
|
|
|
|
Only essential questions with smart defaults:
|
|
|
|
**Question 1: Project Name**
|
|
- "What should I name your mobile project?"
|
|
- Validation: kebab-case or PascalCase, 3-50 chars
|
|
- Example: my-awesome-app or MyAwesomeApp
|
|
|
|
**Question 2: Navigation Setup**
|
|
- "Include Expo Router for navigation?"
|
|
- Default: YES
|
|
- Explain: "File-based routing like Next.js. Recommended for most apps."
|
|
|
|
**Question 3: Testing Setup**
|
|
- "Include testing infrastructure? (Jest + RN Testing Library)"
|
|
- Default: YES
|
|
- Explain: "Recommended for production apps. Connor's 80% coverage standard."
|
|
|
|
**Question 4: EAS Cloud Builds**
|
|
- "Set up EAS for cloud builds and app store submission?"
|
|
- Default: NO (can add later)
|
|
- Explain: "Requires Expo account. Can configure later when ready to deploy."
|
|
|
|
### Step 3: Scaffold with Expo
|
|
|
|
```bash
|
|
npx create-expo-app@latest {project-name} --template blank-typescript
|
|
cd {project-name}
|
|
```
|
|
|
|
**Why Expo?**
|
|
- Used by Instagram, Discord, Shopify
|
|
- Fastest mobile development experience
|
|
- Built-in access to native APIs
|
|
- Over-the-air (OTA) updates
|
|
- Managed workflow (easier) or bare workflow (more control)
|
|
|
|
### Step 4: Install Expo Router (if selected)
|
|
|
|
```bash
|
|
npx expo install expo-router react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar
|
|
```
|
|
|
|
Update `package.json`:
|
|
|
|
```json
|
|
{
|
|
"main": "expo-router/entry"
|
|
}
|
|
```
|
|
|
|
Create `app/_layout.tsx`:
|
|
|
|
```typescript
|
|
import { Stack } from 'expo-router';
|
|
|
|
export default function RootLayout() {
|
|
return <Stack />;
|
|
}
|
|
```
|
|
|
|
Create `app/index.tsx`:
|
|
|
|
```typescript
|
|
import { View, Text, StyleSheet } from 'react-native';
|
|
|
|
export default function Home() {
|
|
return (
|
|
<View style={styles.container}>
|
|
<Text style={styles.title}>Welcome to {project-name}</Text>
|
|
<Text style={styles.subtitle}>
|
|
Edit app/index.tsx to get started
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding: 20,
|
|
},
|
|
title: {
|
|
fontSize: 24,
|
|
fontWeight: 'bold',
|
|
marginBottom: 12,
|
|
},
|
|
subtitle: {
|
|
fontSize: 16,
|
|
color: '#666',
|
|
textAlign: 'center',
|
|
},
|
|
});
|
|
```
|
|
|
|
Update `app.json`:
|
|
|
|
```json
|
|
{
|
|
"expo": {
|
|
"name": "{project-name}",
|
|
"slug": "{project-name}",
|
|
"version": "1.0.0",
|
|
"orientation": "portrait",
|
|
"icon": "./assets/icon.png",
|
|
"userInterfaceStyle": "automatic",
|
|
"scheme": "{project-name}",
|
|
"splash": {
|
|
"image": "./assets/splash.png",
|
|
"resizeMode": "contain",
|
|
"backgroundColor": "#ffffff"
|
|
},
|
|
"assetBundlePatterns": ["**/*"],
|
|
"ios": {
|
|
"supportsTablet": true,
|
|
"bundleIdentifier": "com.{username}.{project-name}"
|
|
},
|
|
"android": {
|
|
"adaptiveIcon": {
|
|
"foregroundImage": "./assets/adaptive-icon.png",
|
|
"backgroundColor": "#ffffff"
|
|
},
|
|
"package": "com.{username}.{project-name}"
|
|
},
|
|
"web": {
|
|
"bundler": "metro",
|
|
"favicon": "./assets/favicon.png"
|
|
},
|
|
"plugins": ["expo-router"]
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 5: Configure TypeScript (Strict Mode)
|
|
|
|
Update `tsconfig.json`:
|
|
|
|
```json
|
|
{
|
|
"extends": "expo/tsconfig.base",
|
|
"compilerOptions": {
|
|
"strict": true,
|
|
"noUnusedLocals": true,
|
|
"noUnusedParameters": true,
|
|
"noFallthroughCasesInSwitch": true,
|
|
"noUncheckedIndexedAccess": true,
|
|
"exactOptionalPropertyTypes": true,
|
|
"noImplicitReturns": true,
|
|
"noImplicitOverride": true,
|
|
"paths": {
|
|
"@/*": ["./*"]
|
|
}
|
|
},
|
|
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"],
|
|
"exclude": ["node_modules"]
|
|
}
|
|
```
|
|
|
|
### Step 6: Set Up Testing (if selected)
|
|
|
|
**6.1 Install Dependencies**
|
|
|
|
```bash
|
|
npm install -D jest jest-expo @testing-library/react-native @testing-library/jest-native @types/jest
|
|
```
|
|
|
|
**6.2 Create `jest.config.js`**
|
|
|
|
```javascript
|
|
module.exports = {
|
|
preset: 'jest-expo',
|
|
setupFilesAfterEnv: ['<rootDir>/__tests__/setup.ts'],
|
|
transformIgnorePatterns: [
|
|
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)',
|
|
],
|
|
collectCoverageFrom: [
|
|
'**/*.{ts,tsx}',
|
|
'!**/coverage/**',
|
|
'!**/node_modules/**',
|
|
'!**/babel.config.js',
|
|
'!**/jest.config.js',
|
|
'!**/__tests__/**',
|
|
],
|
|
coverageThreshold: {
|
|
global: {
|
|
lines: 80,
|
|
functions: 80,
|
|
branches: 80,
|
|
statements: 80,
|
|
},
|
|
},
|
|
};
|
|
```
|
|
|
|
**6.3 Create Test Setup**
|
|
|
|
`__tests__/setup.ts`:
|
|
|
|
```typescript
|
|
import '@testing-library/jest-native/extend-expect';
|
|
|
|
// Mock Expo modules
|
|
jest.mock('expo-font');
|
|
jest.mock('expo-asset');
|
|
|
|
// Silence console warnings in tests
|
|
global.console = {
|
|
...console,
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
};
|
|
```
|
|
|
|
**6.4 Create Example Test**
|
|
|
|
`__tests__/App.test.tsx`:
|
|
|
|
```typescript
|
|
import React from 'react';
|
|
import { render, screen } from '@testing-library/react-native';
|
|
import Home from '../app/index';
|
|
|
|
describe('Home Screen', () => {
|
|
it('should render welcome message when app loads', () => {
|
|
render(<Home />);
|
|
|
|
// Testing Trophy approach: Test user-visible behavior
|
|
expect(screen.getByText(/welcome to/i)).toBeTruthy();
|
|
});
|
|
|
|
it('should display instructions for getting started', () => {
|
|
render(<Home />);
|
|
|
|
expect(screen.getByText(/edit app\/index.tsx/i)).toBeTruthy();
|
|
});
|
|
});
|
|
```
|
|
|
|
**6.5 Update package.json Scripts**
|
|
|
|
```json
|
|
{
|
|
"scripts": {
|
|
"start": "expo start",
|
|
"android": "expo start --android",
|
|
"ios": "expo start --ios",
|
|
"web": "expo start --web",
|
|
"test": "jest --passWithNoTests",
|
|
"test:watch": "jest --watch",
|
|
"test:coverage": "jest --coverage",
|
|
"lint": "eslint . --ext .ts,.tsx",
|
|
"type-check": "tsc --noEmit"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 7: Configure Code Quality Tools
|
|
|
|
**7.1 Set Up ESLint**
|
|
|
|
```bash
|
|
npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-react-native
|
|
```
|
|
|
|
Create `.eslintrc.js`:
|
|
|
|
```javascript
|
|
module.exports = {
|
|
root: true,
|
|
extends: [
|
|
'eslint:recommended',
|
|
'plugin:@typescript-eslint/recommended',
|
|
'plugin:react/recommended',
|
|
'plugin:react-native/all',
|
|
],
|
|
parser: '@typescript-eslint/parser',
|
|
plugins: ['@typescript-eslint', 'react', 'react-native'],
|
|
parserOptions: {
|
|
ecmaFeatures: {
|
|
jsx: true,
|
|
},
|
|
ecmaVersion: 2020,
|
|
sourceType: 'module',
|
|
},
|
|
env: {
|
|
'react-native/react-native': true,
|
|
},
|
|
rules: {
|
|
// Connor's standards
|
|
'no-console': 'warn',
|
|
'no-var': 'error',
|
|
'eqeqeq': ['error', 'always'],
|
|
'prefer-const': 'error',
|
|
'@typescript-eslint/no-unused-vars': [
|
|
'error',
|
|
{ argsIgnorePattern: '^_' },
|
|
],
|
|
'@typescript-eslint/no-explicit-any': 'error',
|
|
'react/react-in-jsx-scope': 'off', // Not needed in React Native
|
|
'react-native/no-inline-styles': 'warn',
|
|
},
|
|
settings: {
|
|
react: {
|
|
version: 'detect',
|
|
},
|
|
},
|
|
};
|
|
```
|
|
|
|
**7.2 Add Prettier**
|
|
|
|
Create `.prettierrc`:
|
|
|
|
```json
|
|
{
|
|
"semi": true,
|
|
"trailingComma": "es5",
|
|
"singleQuote": true,
|
|
"printWidth": 80,
|
|
"tabWidth": 2,
|
|
"useTabs": false
|
|
}
|
|
```
|
|
|
|
Install Prettier:
|
|
|
|
```bash
|
|
npm install -D prettier eslint-config-prettier
|
|
```
|
|
|
|
**7.3 Set Up Husky + lint-staged**
|
|
|
|
```bash
|
|
npx husky-init && npm install
|
|
npm install -D lint-staged
|
|
```
|
|
|
|
Update `.husky/pre-commit`:
|
|
|
|
```bash
|
|
#!/usr/bin/env sh
|
|
. "$(dirname -- "$0")/_/husky.sh"
|
|
|
|
npx lint-staged
|
|
```
|
|
|
|
Create `.lintstagedrc.js`:
|
|
|
|
```javascript
|
|
module.exports = {
|
|
'*.{ts,tsx}': [
|
|
'eslint --fix',
|
|
'prettier --write',
|
|
() => 'tsc --noEmit',
|
|
'jest --bail --findRelatedTests --passWithNoTests',
|
|
],
|
|
'*.{json,md}': ['prettier --write'],
|
|
};
|
|
```
|
|
|
|
### Step 8: Optional EAS Configuration (if selected)
|
|
|
|
```bash
|
|
# Install EAS CLI globally
|
|
npm install -g eas-cli
|
|
|
|
# Login to Expo
|
|
eas login
|
|
|
|
# Configure EAS Build
|
|
eas build:configure
|
|
```
|
|
|
|
This creates `eas.json`:
|
|
|
|
```json
|
|
{
|
|
"cli": {
|
|
"version": ">= 5.9.0"
|
|
},
|
|
"build": {
|
|
"development": {
|
|
"developmentClient": true,
|
|
"distribution": "internal"
|
|
},
|
|
"preview": {
|
|
"distribution": "internal",
|
|
"ios": {
|
|
"simulator": true
|
|
}
|
|
},
|
|
"production": {
|
|
"autoIncrement": true
|
|
}
|
|
},
|
|
"submit": {
|
|
"production": {}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 9: Create Project Documentation
|
|
|
|
**Update README.md**:
|
|
|
|
```markdown
|
|
# {project-name}
|
|
|
|
Cross-platform mobile application built with Expo and React Native.
|
|
|
|
## Features
|
|
|
|
- 📱 React Native (iOS + Android from single codebase)
|
|
- ⚡ Expo SDK 50+ (managed workflow)
|
|
- 🧭 Expo Router (file-based navigation)
|
|
- 🔷 TypeScript (strict mode)
|
|
- 🧪 Testing Trophy approach (Jest + RN Testing Library)
|
|
- ✅ 80% test coverage threshold
|
|
- 🎨 ESLint + Prettier
|
|
- 🪝 Husky pre-commit hooks
|
|
- 🚀 EAS Build & Submit (optional)
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
- Node.js 18+
|
|
- npm 9+
|
|
- iOS Simulator (Mac only) or Android Emulator
|
|
- Expo Go app on physical device (optional)
|
|
|
|
### Installation
|
|
|
|
\`\`\`bash
|
|
npm install
|
|
\`\`\`
|
|
|
|
### Development
|
|
|
|
\`\`\`bash
|
|
npm start
|
|
\`\`\`
|
|
|
|
This opens the Expo Dev Server. From there:
|
|
|
|
- Press **i** to open iOS simulator
|
|
- Press **a** to open Android emulator
|
|
- Scan QR code with Expo Go app on your phone
|
|
|
|
Or run directly:
|
|
|
|
\`\`\`bash
|
|
npm run ios # iOS simulator
|
|
npm run android # Android emulator
|
|
npm run web # Web browser
|
|
\`\`\`
|
|
|
|
## Project Structure
|
|
|
|
\`\`\`
|
|
app/ # Expo Router (file-based routing)
|
|
├── _layout.tsx # Root layout
|
|
├── index.tsx # Home screen
|
|
└── (tabs)/ # Tab navigation (if using)
|
|
components/ # Reusable components
|
|
├── ui/ # UI components
|
|
└── features/ # Feature components
|
|
__tests__/ # Test files
|
|
├── setup.ts # Test configuration
|
|
└── App.test.tsx # Example test
|
|
assets/ # Images, fonts, etc.
|
|
├── icon.png
|
|
├── splash.png
|
|
└── adaptive-icon.png
|
|
\`\`\`
|
|
|
|
## Available Commands
|
|
|
|
### Development
|
|
- \`npm start\` - Start Expo dev server
|
|
- \`npm run ios\` - Run on iOS simulator
|
|
- \`npm run android\` - Run on Android emulator
|
|
- \`npm run web\` - Run in web browser
|
|
|
|
### Code Quality
|
|
- \`npm run lint\` - Lint code with ESLint
|
|
- \`npm run type-check\` - Check TypeScript types
|
|
|
|
### Testing
|
|
- \`npm test\` - Run all tests
|
|
- \`npm run test:watch\` - Run tests in watch mode
|
|
- \`npm run test:coverage\` - Run tests with coverage report
|
|
|
|
### Build & Deploy (if EAS configured)
|
|
- \`eas build --platform ios\` - Build for iOS
|
|
- \`eas build --platform android\` - Build for Android
|
|
- \`eas build --platform all\` - Build for both platforms
|
|
- \`eas submit --platform ios\` - Submit to App Store
|
|
- \`eas submit --platform android\` - Submit to Play Store
|
|
|
|
## Testing Strategy
|
|
|
|
This project follows the **Testing Trophy** approach:
|
|
|
|
- **70% Integration Tests**: User workflows and component interactions
|
|
- **20% Unit Tests**: Complex business logic
|
|
- **10% E2E Tests**: Critical user journeys (use Detox or Maestro)
|
|
|
|
### Writing Tests
|
|
|
|
Test file naming: \`[component-name].test.tsx\`
|
|
|
|
\`\`\`typescript
|
|
import { render, screen, fireEvent } from '@testing-library/react-native';
|
|
|
|
describe('LoginScreen', () => {
|
|
it('should submit form when user enters valid credentials', () => {
|
|
const onSubmit = jest.fn();
|
|
render(<LoginScreen onSubmit={onSubmit} />);
|
|
|
|
fireEvent.changeText(screen.getByPlaceholderText('Email'), 'test@example.com');
|
|
fireEvent.changeText(screen.getByPlaceholderText('Password'), 'password123');
|
|
fireEvent.press(screen.getByText('Login'));
|
|
|
|
expect(onSubmit).toHaveBeenCalled();
|
|
});
|
|
});
|
|
\`\`\`
|
|
|
|
## Navigation with Expo Router
|
|
|
|
File-based routing like Next.js:
|
|
|
|
\`\`\`
|
|
app/
|
|
├── _layout.tsx → Root layout
|
|
├── index.tsx → / (home)
|
|
├── about.tsx → /about
|
|
├── users/
|
|
│ ├── [id].tsx → /users/:id (dynamic)
|
|
│ └── index.tsx → /users
|
|
└── (tabs)/ → Tab navigation group
|
|
├── _layout.tsx
|
|
├── home.tsx
|
|
└── profile.tsx
|
|
\`\`\`
|
|
|
|
Navigate programmatically:
|
|
|
|
\`\`\`typescript
|
|
import { router } from 'expo-router';
|
|
|
|
router.push('/about');
|
|
router.replace('/login');
|
|
router.back();
|
|
\`\`\`
|
|
|
|
## Environment Variables
|
|
|
|
Create \`.env\`:
|
|
|
|
\`\`\`env
|
|
API_URL=https://api.example.com
|
|
\`\`\`
|
|
|
|
Install and configure:
|
|
|
|
\`\`\`bash
|
|
npm install react-native-dotenv
|
|
\`\`\`
|
|
|
|
## Building for Production
|
|
|
|
### With EAS (Recommended)
|
|
|
|
\`\`\`bash
|
|
# Build for iOS (requires Apple Developer account)
|
|
eas build --platform ios --profile production
|
|
|
|
# Build for Android (requires Google Play Console account)
|
|
eas build --platform android --profile production
|
|
|
|
# Submit to stores
|
|
eas submit --platform ios
|
|
eas submit --platform android
|
|
\`\`\`
|
|
|
|
### Local Builds
|
|
|
|
\`\`\`bash
|
|
# iOS (requires Mac + Xcode)
|
|
npx expo run:ios --configuration Release
|
|
|
|
# Android
|
|
npx expo run:android --variant release
|
|
\`\`\`
|
|
|
|
## Over-the-Air (OTA) Updates
|
|
|
|
Expo allows instant updates without app store review:
|
|
|
|
\`\`\`bash
|
|
# Publish update to production
|
|
eas update --branch production --message "Bug fixes"
|
|
|
|
# Users get update on next app restart
|
|
\`\`\`
|
|
|
|
## Deployment Checklist
|
|
|
|
- [ ] Update \`version\` in app.json
|
|
- [ ] Test on physical iOS device
|
|
- [ ] Test on physical Android device
|
|
- [ ] Run full test suite: \`npm run test:coverage\`
|
|
- [ ] Check bundle size: \`npx expo export\`
|
|
- [ ] Update app screenshots
|
|
- [ ] Build for production: \`eas build --platform all\`
|
|
- [ ] Test production builds
|
|
- [ ] Submit to stores: \`eas submit --platform all\`
|
|
|
|
## Common Issues
|
|
|
|
### Metro bundler cache issues
|
|
\`\`\`bash
|
|
npm start -- --clear
|
|
\`\`\`
|
|
|
|
### iOS simulator not opening
|
|
\`\`\`bash
|
|
sudo xcode-select -s /Applications/Xcode.app
|
|
\`\`\`
|
|
|
|
### Android emulator issues
|
|
- Ensure Android Studio is installed
|
|
- Check emulator is running: \`adb devices\`
|
|
|
|
## Resources
|
|
|
|
- [Expo Documentation](https://docs.expo.dev)
|
|
- [Expo Router](https://docs.expo.dev/router/introduction/)
|
|
- [React Native Testing Library](https://callstack.github.io/react-native-testing-library/)
|
|
- [EAS Build](https://docs.expo.dev/build/introduction/)
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
---
|
|
|
|
Built with [react-project-scaffolder](https://github.com/yourusername/react-project-scaffolder)
|
|
```
|
|
|
|
### Step 10: Initialize Git
|
|
|
|
```bash
|
|
git init
|
|
git add .
|
|
git commit -m "feat: initial Expo + React Native setup with testing
|
|
|
|
- Expo SDK 50+ with managed workflow
|
|
- Expo Router for navigation
|
|
- TypeScript strict mode
|
|
- Jest + React Native Testing Library (80% coverage)
|
|
- ESLint + Prettier + Husky
|
|
- EAS configuration (optional)
|
|
- Comprehensive documentation"
|
|
```
|
|
|
|
Ensure `.gitignore` includes:
|
|
|
|
```
|
|
node_modules/
|
|
.expo/
|
|
dist/
|
|
npm-debug.*
|
|
*.jks
|
|
*.p8
|
|
*.p12
|
|
*.key
|
|
*.mobileprovision
|
|
*.orig.*
|
|
web-build/
|
|
|
|
# macOS
|
|
.DS_Store
|
|
|
|
# Environment
|
|
.env
|
|
.env.local
|
|
|
|
# Testing
|
|
coverage/
|
|
```
|
|
|
|
### Step 11: Verify Setup
|
|
|
|
```bash
|
|
# Verify all files
|
|
ls -la
|
|
|
|
# Check Expo installation
|
|
npx expo --version
|
|
|
|
# Verify tests run
|
|
npm test
|
|
|
|
# Start Expo dev server (verify it works)
|
|
npm start
|
|
```
|
|
|
|
### Step 12: Provide User Instructions
|
|
|
|
**Display to user**:
|
|
|
|
```markdown
|
|
✅ Mobile project "{project-name}" created successfully!
|
|
|
|
📁 Location: ./{project-name}
|
|
|
|
🚀 Next steps:
|
|
|
|
1. cd {project-name}
|
|
2. npm install
|
|
3. npm start
|
|
|
|
Then:
|
|
- Press 'i' for iOS simulator
|
|
- Press 'a' for Android emulator
|
|
- Scan QR code with Expo Go app on your phone
|
|
|
|
📚 What you have:
|
|
✓ Expo SDK 50+ (managed workflow)
|
|
✓ Expo Router (file-based navigation)
|
|
✓ TypeScript strict mode
|
|
✓ Jest + React Native Testing Library (80% coverage)
|
|
✓ ESLint + Prettier + Husky
|
|
✓ EAS Build configuration (if selected)
|
|
✓ OTA update support
|
|
✓ Comprehensive documentation
|
|
|
|
🧪 Test your setup:
|
|
npm test # Run all tests
|
|
npm run lint # Check code quality
|
|
npm run type-check # Verify types
|
|
|
|
📱 Running on devices:
|
|
- Install "Expo Go" app from App Store / Play Store
|
|
- Scan QR code from terminal
|
|
- See changes instantly with Fast Refresh
|
|
|
|
🔄 Pre-commit hooks active:
|
|
- Linting (auto-fix)
|
|
- Formatting (auto-format)
|
|
- Type checking
|
|
- Related tests run automatically
|
|
|
|
📦 Build for production (if EAS configured):
|
|
eas build --platform all
|
|
eas submit --platform all
|
|
|
|
💡 Tips:
|
|
- Use Expo Router for navigation (like Next.js)
|
|
- Test on physical devices early and often
|
|
- Use EAS for cloud builds (no Xcode/Android Studio needed)
|
|
- OTA updates allow instant bug fixes without app store review
|
|
|
|
📖 Documentation:
|
|
- README.md - Complete guide with all commands
|
|
- Expo docs: https://docs.expo.dev
|
|
|
|
🎯 Production-ready mobile development!
|
|
```
|
|
|
|
---
|
|
|
|
## File Structure Output
|
|
|
|
```
|
|
{project-name}/
|
|
├── .husky/
|
|
│ └── pre-commit # Pre-commit hooks
|
|
├── app/
|
|
│ ├── _layout.tsx # Root layout (Expo Router)
|
|
│ └── index.tsx # Home screen
|
|
├── assets/
|
|
│ ├── icon.png # App icon
|
|
│ ├── splash.png # Splash screen
|
|
│ └── adaptive-icon.png # Android adaptive icon
|
|
├── components/
|
|
│ ├── ui/ # UI components
|
|
│ └── features/ # Feature components
|
|
├── __tests__/
|
|
│ ├── setup.ts # Test setup
|
|
│ └── App.test.tsx # Example test
|
|
├── .eslintrc.js # ESLint config
|
|
├── .prettierrc # Prettier config
|
|
├── .gitignore # Git ignore
|
|
├── .lintstagedrc.js # lint-staged config
|
|
├── jest.config.js # Jest config
|
|
├── tsconfig.json # TypeScript config
|
|
├── app.json # Expo config
|
|
├── package.json # Dependencies
|
|
├── eas.json # EAS Build config (if configured)
|
|
└── README.md # Documentation
|
|
```
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
- [ ] Expo project scaffolded successfully
|
|
- [ ] Expo Router configured (if selected)
|
|
- [ ] TypeScript strict mode enabled
|
|
- [ ] Jest + RN Testing Library configured
|
|
- [ ] Example test passes
|
|
- [ ] ESLint + Prettier configured
|
|
- [ ] Husky pre-commit hooks working
|
|
- [ ] EAS configured (if selected)
|
|
- [ ] README generated
|
|
- [ ] Git initialized with commit
|
|
- [ ] `npm start` opens Expo Dev Server
|
|
- [ ] QR code displays for device testing
|
|
- [ ] Setup time < 90 seconds (excluding npm install)
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
**Issue**: Expo CLI not found
|
|
**Solution**: Use npx: `npx expo start`
|
|
|
|
**Issue**: Metro bundler cache issues
|
|
**Solution**: Clear cache: `npm start -- --clear`
|
|
|
|
**Issue**: Tests fail with React Native module errors
|
|
**Solution**: Check jest.config.js transformIgnorePatterns
|
|
|
|
**Issue**: iOS simulator won't open
|
|
**Solution**: Set Xcode path: `sudo xcode-select -s /Applications/Xcode.app`
|
|
|
|
**Issue**: Android emulator not detected
|
|
**Solution**: Check ADB: `adb devices`, ensure emulator is running
|
|
|
|
**Issue**: EAS build fails
|
|
**Solution**: Check credentials and app config in app.json
|
|
|
|
---
|
|
|
|
## Why This Tech Stack?
|
|
|
|
**Expo over bare React Native**:
|
|
- Faster development (managed workflow)
|
|
- Built-in access to native APIs
|
|
- OTA updates (instant bug fixes)
|
|
- Used by Instagram, Discord, Shopify
|
|
- Easier for beginners, powerful for pros
|
|
|
|
**Expo Router over React Navigation**:
|
|
- File-based routing (like Next.js)
|
|
- Better TypeScript support
|
|
- Deep linking built-in
|
|
- Less boilerplate
|
|
|
|
**EAS Build over local builds**:
|
|
- No need for Xcode/Android Studio
|
|
- Cloud-based builds
|
|
- Consistent environments
|
|
- Easy team collaboration
|
|
|
|
---
|
|
|
|
**Remember**: This mode delivers native mobile performance with React developer experience. Production-ready with Connor's standards applied to mobile development.
|