--- name: react-native-platform user-invocable: false description: Use when handling platform-specific code in React Native for iOS and Android. Covers Platform API, platform-specific components, native modules, and cross-platform best practices. allowed-tools: - Read - Write - Edit - Bash - Grep - Glob --- # React Native Platform APIs Use this skill when writing platform-specific code for iOS and Android, handling platform differences, and accessing native functionality. ## Key Concepts ### Platform Detection Detect the current platform: ```tsx import { Platform } from 'react-native'; // Simple check if (Platform.OS === 'ios') { console.log('Running on iOS'); } else if (Platform.OS === 'android') { console.log('Running on Android'); } // Platform.select const styles = StyleSheet.create({ container: { ...Platform.select({ ios: { paddingTop: 20, }, android: { paddingTop: 0, }, }), }, }); // Get platform version console.log(`Android API Level: ${Platform.Version}`); // Android console.log(`iOS Version: ${Platform.Version}`); // iOS ``` ### Platform-Specific Files Create platform-specific files: ``` components/ Button.ios.tsx # iOS implementation Button.android.tsx # Android implementation Button.tsx # Shared/default implementation ``` ```tsx // Button.ios.tsx import React from 'react'; import { View, Text } from 'react-native'; export default function Button({ title, onPress }: ButtonProps) { return ( {title} ); } // Button.android.tsx import React from 'react'; import { View, Text } from 'react-native'; export default function Button({ title, onPress }: ButtonProps) { return ( {title} ); } // Usage - React Native automatically picks the right file import Button from './components/Button'; ``` ### Platform-Specific Components Use platform-specific components: ```tsx import { Platform, StatusBar, TouchableOpacity, TouchableNativeFeedback, View, } from 'react-native'; // StatusBar // Touchable components const Touchable = Platform.OS === 'android' ? TouchableNativeFeedback : TouchableOpacity; console.log('Pressed')}> Press Me ``` ## Best Practices ### Use Platform.select for Inline Differences ```tsx import { Platform, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ button: { padding: Platform.select({ ios: 12, android: 16, default: 12, }), fontFamily: Platform.select({ ios: 'System', android: 'Roboto', default: 'System', }), }, }); ``` ### Handle Safe Areas Properly handle notches and safe areas: ```tsx import { SafeAreaView, Platform, StyleSheet } from 'react-native'; export default function App() { return ( {/* Content */} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', // Additional padding for Android status bar paddingTop: Platform.OS === 'android' ? 25 : 0, }, }); ``` ### Permissions Handling Request platform-specific permissions: ```tsx import { Platform, PermissionsAndroid, Alert } from 'react-native'; async function requestCameraPermission() { if (Platform.OS === 'android') { try { const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.CAMERA, { title: 'Camera Permission', message: 'App needs access to your camera', buttonNeutral: 'Ask Me Later', buttonNegative: 'Cancel', buttonPositive: 'OK', } ); return granted === PermissionsAndroid.RESULTS.GRANTED; } catch (err) { console.warn(err); return false; } } else { // iOS permissions handled via Info.plist return true; } } ``` ### Back Button Handling (Android) Handle Android hardware back button: ```tsx import { useEffect } from 'react'; import { BackHandler, Platform, Alert } from 'react-native'; function useBackHandler(handler: () => boolean) { useEffect(() => { if (Platform.OS !== 'android') return; const backHandler = BackHandler.addEventListener( 'hardwareBackPress', handler ); return () => backHandler.remove(); }, [handler]); } // Usage function MyScreen() { useBackHandler(() => { Alert.alert('Exit App', 'Are you sure you want to exit?', [ { text: 'Cancel', style: 'cancel' }, { text: 'Exit', onPress: () => BackHandler.exitApp() }, ]); return true; // Prevent default behavior }); return ; } ``` ## Common Patterns ### Adaptive Components Create components that adapt to platform: ```tsx import React from 'react'; import { Platform, View, Text, StyleSheet, TouchableOpacity, TouchableNativeFeedback, } from 'react-native'; interface ButtonProps { title: string; onPress: () => void; } export default function AdaptiveButton({ title, onPress }: ButtonProps) { if (Platform.OS === 'android') { return ( {title} ); } return ( {title} ); } const styles = StyleSheet.create({ androidButton: { backgroundColor: '#2196F3', padding: 16, borderRadius: 4, elevation: 4, }, androidText: { color: '#fff', fontSize: 16, fontWeight: 'bold', textAlign: 'center', textTransform: 'uppercase', }, iosButton: { backgroundColor: '#007AFF', padding: 14, borderRadius: 8, }, iosText: { color: '#fff', fontSize: 17, fontWeight: '600', textAlign: 'center', }, }); ``` ### Platform-Specific Navigation ```tsx import { Platform } from 'react-native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); export default function AppNavigator() { return ( ); } ``` ### Linking to Native Apps ```tsx import { Linking, Platform, Alert } from 'react-native'; async function openMaps(address: string) { const url = Platform.select({ ios: `maps://app?address=${encodeURIComponent(address)}`, android: `geo:0,0?q=${encodeURIComponent(address)}`, }); if (!url) return; const supported = await Linking.canOpenURL(url); if (supported) { await Linking.openURL(url); } else { Alert.alert('Error', 'Cannot open maps application'); } } async function openPhoneDialer(phoneNumber: string) { const url = `tel:${phoneNumber}`; const supported = await Linking.canOpenURL(url); if (supported) { await Linking.openURL(url); } else { Alert.alert('Error', 'Cannot open phone dialer'); } } async function openEmail(email: string, subject?: string) { const url = `mailto:${email}${subject ? `?subject=${encodeURIComponent(subject)}` : ''}`; const supported = await Linking.canOpenURL(url); if (supported) { await Linking.openURL(url); } else { Alert.alert('Error', 'Cannot open email client'); } } ``` ### Keyboard Avoiding View ```tsx import React from 'react'; import { KeyboardAvoidingView, Platform, ScrollView, TextInput, StyleSheet, } from 'react-native'; export default function FormScreen() { return ( ); } const styles = StyleSheet.create({ container: { flex: 1, }, input: { height: 50, borderWidth: 1, borderColor: '#ccc', borderRadius: 8, paddingHorizontal: 16, marginVertical: 8, }, }); ``` ### Status Bar Configuration ```tsx import React from 'react'; import { StatusBar, Platform, SafeAreaView } from 'react-native'; export default function App() { return ( <> {/* App content */} ); } ``` ## Anti-Patterns ### Don't Use Platform Checks for Styling Only ```tsx // Bad - Inline platform checks Content // Good - Use Platform.select in StyleSheet const styles = StyleSheet.create({ container: { ...Platform.select({ ios: { padding: 12, marginTop: 0, }, android: { padding: 16, marginTop: 20, }, }), }, }); Content ``` ### Don't Forget Android Back Button ```tsx // Bad - No back button handling function MyScreen() { return ; } // Good - Handle back button function MyScreen({ navigation }: any) { useEffect(() => { if (Platform.OS !== 'android') return; const backHandler = BackHandler.addEventListener( 'hardwareBackPress', () => { navigation.goBack(); return true; } ); return () => backHandler.remove(); }, [navigation]); return ; } ``` ### Don't Hardcode Platform Values ```tsx // Bad - Magic numbers Content // Good - Use constants or safe area import { useSafeAreaInsets } from 'react-native-safe-area-context'; function MyComponent() { const insets = useSafeAreaInsets(); return ( Content ); } ``` ### Don't Assume Platform Features ```tsx // Bad - Assuming feature exists await Linking.openURL('mailto:test@example.com'); // Good - Check if supported const url = 'mailto:test@example.com'; const supported = await Linking.canOpenURL(url); if (supported) { await Linking.openURL(url); } else { Alert.alert('Error', 'Email client not available'); } ``` ## Related Skills - **react-native-components**: Building platform-aware components - **react-native-styling**: Platform-specific styling - **react-native-native-modules**: Building custom native modules