Mobile Expert

The Mobile Expert agent specializes in mobile application development for iOS, Android, and cross-platform solutions.

Expertise Areas#

  • React Native - Components, navigation, native modules
  • Expo - Managed workflow, EAS Build, OTA updates
  • Flutter - Widgets, state management, platform channels
  • Native Development - Swift/SwiftUI, Kotlin/Jetpack Compose
  • Mobile Patterns - Offline-first, push notifications, deep linking
  • App Store - Submission, guidelines, optimization

Usage Examples#

React Native App#

Use the mobile-expert agent to create a React Native app structure for a food delivery app.

Response includes:

  • Project structure
  • Navigation setup
  • State management
  • API integration patterns

Expo Setup#

Use the mobile-expert agent to set up Expo with EAS Build for our team.

Response includes:

  • Expo configuration
  • EAS Build setup
  • Environment management
  • OTA update strategy

Mobile UI#

Use the mobile-expert agent to build a swipeable card component like Tinder.

Response includes:

  • Gesture handling
  • Animation implementation
  • Performance optimization
  • Platform-specific code

React Native Patterns#

Project Structure#

src/ ├── app/ # App entry, providers ├── components/ # Reusable components │ ├── ui/ # Base UI components │ └── features/ # Feature-specific components ├── screens/ # Screen components ├── navigation/ # Navigation configuration ├── hooks/ # Custom hooks ├── services/ # API, storage, etc. ├── store/ # State management ├── utils/ # Utilities ├── types/ # TypeScript types └── constants/ # App constants
1// navigation/RootNavigator.tsx 2import { NavigationContainer } from '@react-navigation/native'; 3import { createNativeStackNavigator } from '@react-navigation/native-stack'; 4import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; 5 6const Stack = createNativeStackNavigator<RootStackParamList>(); 7const Tab = createBottomTabNavigator<MainTabParamList>(); 8 9function MainTabs() { 10 return ( 11 <Tab.Navigator 12 screenOptions={({ route }) => ({ 13 tabBarIcon: ({ focused, color, size }) => { 14 const icons = { 15 Home: focused ? 'home' : 'home-outline', 16 Search: focused ? 'search' : 'search-outline', 17 Profile: focused ? 'person' : 'person-outline', 18 }; 19 return <Ionicons name={icons[route.name]} size={size} color={color} />; 20 }, 21 })} 22 > 23 <Tab.Screen name="Home" component={HomeScreen} /> 24 <Tab.Screen name="Search" component={SearchScreen} /> 25 <Tab.Screen name="Profile" component={ProfileScreen} /> 26 </Tab.Navigator> 27 ); 28} 29 30export function RootNavigator() { 31 const { isAuthenticated } = useAuth(); 32 33 return ( 34 <NavigationContainer> 35 <Stack.Navigator screenOptions={{ headerShown: false }}> 36 {!isAuthenticated ? ( 37 <Stack.Screen name="Auth" component={AuthNavigator} /> 38 ) : ( 39 <> 40 <Stack.Screen name="Main" component={MainTabs} /> 41 <Stack.Screen name="Details" component={DetailsScreen} /> 42 </> 43 )} 44 </Stack.Navigator> 45 </NavigationContainer> 46 ); 47}

Custom Hooks#

1// hooks/useApi.ts 2export function useApi<T>( 3 fetcher: () => Promise<T>, 4 deps: any[] = [] 5) { 6 const [data, setData] = useState<T | null>(null); 7 const [loading, setLoading] = useState(true); 8 const [error, setError] = useState<Error | null>(null); 9 10 useEffect(() => { 11 let cancelled = false; 12 13 setLoading(true); 14 fetcher() 15 .then((result) => { 16 if (!cancelled) { 17 setData(result); 18 setError(null); 19 } 20 }) 21 .catch((err) => { 22 if (!cancelled) { 23 setError(err); 24 } 25 }) 26 .finally(() => { 27 if (!cancelled) { 28 setLoading(false); 29 } 30 }); 31 32 return () => { 33 cancelled = true; 34 }; 35 }, deps); 36 37 return { data, loading, error, refetch: () => fetcher() }; 38} 39 40// hooks/useKeyboard.ts 41export function useKeyboard() { 42 const [keyboardHeight, setKeyboardHeight] = useState(0); 43 const [isKeyboardVisible, setIsKeyboardVisible] = useState(false); 44 45 useEffect(() => { 46 const showListener = Keyboard.addListener('keyboardDidShow', (e) => { 47 setKeyboardHeight(e.endCoordinates.height); 48 setIsKeyboardVisible(true); 49 }); 50 51 const hideListener = Keyboard.addListener('keyboardDidHide', () => { 52 setKeyboardHeight(0); 53 setIsKeyboardVisible(false); 54 }); 55 56 return () => { 57 showListener.remove(); 58 hideListener.remove(); 59 }; 60 }, []); 61 62 return { keyboardHeight, isKeyboardVisible }; 63}

Expo Configuration#

app.json / app.config.js#

1// app.config.js 2export default { 3 expo: { 4 name: 'MyApp', 5 slug: 'my-app', 6 version: '1.0.0', 7 orientation: 'portrait', 8 icon: './assets/icon.png', 9 splash: { 10 image: './assets/splash.png', 11 resizeMode: 'contain', 12 backgroundColor: '#ffffff', 13 }, 14 ios: { 15 bundleIdentifier: 'com.mycompany.myapp', 16 supportsTablet: true, 17 config: { 18 usesNonExemptEncryption: false, 19 }, 20 }, 21 android: { 22 package: 'com.mycompany.myapp', 23 adaptiveIcon: { 24 foregroundImage: './assets/adaptive-icon.png', 25 backgroundColor: '#ffffff', 26 }, 27 }, 28 plugins: [ 29 'expo-router', 30 [ 31 'expo-notifications', 32 { 33 icon: './assets/notification-icon.png', 34 color: '#ffffff', 35 }, 36 ], 37 ], 38 extra: { 39 apiUrl: process.env.API_URL, 40 eas: { 41 projectId: 'your-project-id', 42 }, 43 }, 44 }, 45};

EAS Build Configuration#

1// eas.json 2{ 3 "cli": { 4 "version": ">= 5.0.0" 5 }, 6 "build": { 7 "development": { 8 "developmentClient": true, 9 "distribution": "internal", 10 "ios": { 11 "simulator": true 12 } 13 }, 14 "preview": { 15 "distribution": "internal", 16 "channel": "preview" 17 }, 18 "production": { 19 "channel": "production" 20 } 21 }, 22 "submit": { 23 "production": { 24 "ios": { 25 "appleId": "your@email.com", 26 "ascAppId": "123456789" 27 }, 28 "android": { 29 "serviceAccountKeyPath": "./google-service-account.json" 30 } 31 } 32 } 33}

Mobile-Specific Patterns#

Offline-First#

1// services/offline.ts 2import NetInfo from '@react-native-community/netinfo'; 3import AsyncStorage from '@react-native-async-storage/async-storage'; 4 5class OfflineQueue { 6 private queue: Array<{ action: string; data: any }> = []; 7 8 async addToQueue(action: string, data: any) { 9 this.queue.push({ action, data }); 10 await AsyncStorage.setItem('offline_queue', JSON.stringify(this.queue)); 11 } 12 13 async processQueue() { 14 const state = await NetInfo.fetch(); 15 if (!state.isConnected) return; 16 17 const stored = await AsyncStorage.getItem('offline_queue'); 18 if (!stored) return; 19 20 const queue = JSON.parse(stored); 21 for (const item of queue) { 22 try { 23 await this.processItem(item); 24 } catch (error) { 25 console.error('Failed to process queue item:', error); 26 } 27 } 28 29 await AsyncStorage.removeItem('offline_queue'); 30 } 31 32 private async processItem(item: { action: string; data: any }) { 33 // Process based on action type 34 } 35}

Push Notifications#

1// services/notifications.ts 2import * as Notifications from 'expo-notifications'; 3 4Notifications.setNotificationHandler({ 5 handleNotification: async () => ({ 6 shouldShowAlert: true, 7 shouldPlaySound: true, 8 shouldSetBadge: true, 9 }), 10}); 11 12export async function registerForPushNotifications() { 13 const { status: existingStatus } = await Notifications.getPermissionsAsync(); 14 let finalStatus = existingStatus; 15 16 if (existingStatus !== 'granted') { 17 const { status } = await Notifications.requestPermissionsAsync(); 18 finalStatus = status; 19 } 20 21 if (finalStatus !== 'granted') { 22 throw new Error('Permission not granted'); 23 } 24 25 const token = await Notifications.getExpoPushTokenAsync(); 26 return token.data; 27}

Sample Prompts#

TaskPrompt
Setup"Set up a React Native app with Expo and TypeScript"
Navigation"Create a stack + tab navigator with auth flow"
Gestures"Build a pull-to-refresh list with smooth animations"
Notifications"Implement push notifications with deep linking"
Performance"Optimize FlatList for 1000+ items"

Configuration#

1// bootspring.config.js 2module.exports = { 3 agents: { 4 customInstructions: { 5 'mobile-expert': ` 6 - Use Expo managed workflow when possible 7 - Follow platform-specific guidelines 8 - Optimize for performance and battery 9 - Handle offline scenarios 10 - Test on both iOS and Android 11 `, 12 }, 13 }, 14};