--- name: mobile-developer description: Expert in React Native, Expo, and cross-platform mobile development version: 1.0.0 tags: [mobile, react-native, expo, ios, android, cross-platform] --- # Mobile Developer Skill I help you build cross-platform mobile apps with React Native and Expo. ## What I Do **App Development:** - React Native / Expo apps (iOS + Android) - Navigation and routing - State management - API integration **Native Features:** - Camera, location, notifications - Biometric authentication - File system access - Device sensors **Performance:** - Optimize bundle size - Lazy loading - Image optimization - Memory management **Distribution:** - App Store / Google Play submission - Over-the-air (OTA) updates - Beta testing (TestFlight, internal testing) ## Quick Start: Expo App ### Create New App ```bash # Create Expo app npx create-expo-app my-app --template blank-typescript cd my-app # Install dependencies npx expo install react-native-screens react-native-safe-area-context npx expo install expo-router # Start development npx expo start ``` ### Project Structure ``` my-app/ ├── app/ │ ├── (tabs)/ │ │ ├── index.tsx # Home tab │ │ ├── profile.tsx # Profile tab │ │ └── _layout.tsx # Tab layout │ ├── users/ │ │ └── [id].tsx # Dynamic route │ ├── _layout.tsx # Root layout │ └── +not-found.tsx # 404 page ├── components/ │ ├── Button.tsx │ ├── Card.tsx │ └── Loading.tsx ├── hooks/ │ └── useAuth.ts ├── app.json └── package.json ``` --- ## Navigation with Expo Router ### Tab Navigation ```typescript // app/(tabs)/_layout.tsx import { Tabs } from 'expo-router' import { Ionicons } from '@expo/vector-icons' export default function TabLayout() { return ( ( ) }} /> ( ) }} /> ) } ``` ### Stack Navigation ```typescript // app/users/[id].tsx import { useLocalSearchParams } from 'expo-router' import { View, Text } from 'react-native' export default function UserDetail() { const { id } = useLocalSearchParams() return ( User ID: {id} ) } ``` --- ## UI Components ### Custom Button ```typescript // components/Button.tsx import { TouchableOpacity, Text, StyleSheet, ActivityIndicator } from 'react-native' interface ButtonProps { title: string onPress: () => void variant?: 'primary' | 'secondary' loading?: boolean disabled?: boolean } export function Button({ title, onPress, variant = 'primary', loading = false, disabled = false }: ButtonProps) { return ( {loading ? ( ) : ( {title} )} ) } const styles = StyleSheet.create({ button: { padding: 16, borderRadius: 8, alignItems: 'center', justifyContent: 'center' }, primary: { backgroundColor: '#007AFF' }, secondary: { backgroundColor: '#8E8E93' }, disabled: { opacity: 0.5 }, text: { color: '#fff', fontSize: 16, fontWeight: '600' } }) ``` ### Card Component ```typescript // components/Card.tsx import { View, Text, StyleSheet, TouchableOpacity } from 'react-native' import { ReactNode } from 'react' interface CardProps { title?: string children: ReactNode onPress?: () => void } export function Card({ title, children, onPress }: CardProps) { const Container = onPress ? TouchableOpacity : View return ( {title && {title}} {children} ) } const styles = StyleSheet.create({ card: { backgroundColor: '#fff', borderRadius: 12, padding: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3 }, title: { fontSize: 18, fontWeight: '600', marginBottom: 12 } }) ``` --- ## Data Fetching ### Custom Hook ```typescript // hooks/useQuery.ts import { useState, useEffect } from 'react' interface UseQueryResult { data: T | null loading: boolean error: Error | null refetch: () => void } export function useQuery(url: string): UseQueryResult { const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const fetchData = async () => { try { setLoading(true) const response = await fetch(url) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const json = await response.json() setData(json) setError(null) } catch (e) { setError(e as Error) } finally { setLoading(false) } } useEffect(() => { fetchData() }, [url]) return { data, loading, error, refetch: fetchData } } ``` ### Usage ```typescript // app/(tabs)/index.tsx import { View, Text, FlatList, RefreshControl } from 'react-native' import { useQuery } from '@/hooks/useQuery' import { Card } from '@/components/Card' interface Post { id: string title: string content: string } export default function HomeScreen() { const { data, loading, error, refetch } = useQuery( 'https://api.example.com/posts' ) if (error) { return ( Error: {error.message} ) } return ( item.id} renderItem={({ item }) => ( {item.content} )} refreshControl={ } /> ) } ``` --- ## Native Features ### Camera ```typescript // app/camera.tsx import { Camera, CameraType } from 'expo-camera' import { useState } from 'react' import { Button, View, StyleSheet } from 'react-native' export default function CameraScreen() { const [type, setType] = useState(CameraType.back) const [permission, requestPermission] = Camera.useCameraPermissions() if (!permission) { return } if (!permission.granted) { return (