expo-framework-rule

安装量: 58
排名: #12761

安装

npx skills add https://github.com/oimiragieo/agent-studio --skill expo-framework-rule

Expo Framework Rule Skill Expo SDK Features and APIs Core Expo Modules FileSystem: import * as FileSystem from 'expo-file-system' ; // Read file const content = await FileSystem . readAsStringAsync ( FileSystem . documentDirectory + 'file.txt' ) ; // Download file const download = await FileSystem . downloadAsync ( 'https://example.com/file.pdf' , FileSystem . documentDirectory + 'file.pdf' ) ; Camera: import { CameraView , useCameraPermissions } from 'expo-camera' ; function CameraScreen ( ) { const [ permission , requestPermission ] = useCameraPermissions ( ) ; if ( ! permission ?. granted ) { return < Button onPress = { requestPermission } title = "Grant Permission" /

; } return ( < CameraView style = { styles . camera } onBarcodeScanned = { ( { data } ) => console . log ( data ) } /

) ; } Location: import * as Location from 'expo-location' ; const getLocation = async ( ) => { const { status } = await Location . requestForegroundPermissionsAsync ( ) ; if ( status !== 'granted' ) { return ; } const location = await Location . getCurrentPositionAsync ( { accuracy : Location . Accuracy . High , } ) ; return location . coords ; } ; Notifications: import * as Notifications from 'expo-notifications' ; // Configure notification handler Notifications . setNotificationHandler ( { handleNotification : async ( ) => ( { shouldShowAlert : true , shouldPlaySound : true , shouldSetBadge : true , } ) , } ) ; // Schedule notification await Notifications . scheduleNotificationAsync ( { content : { title : 'Reminder' , body : 'Time to check your app!' , } , trigger : { seconds : 60 } , } ) ; Asset Management import { Image } from 'expo-image' ; import { Asset } from 'expo-asset' ; // Preload assets await Asset . loadAsync ( [ require ( './assets/logo.png' ) , require ( './assets/background.jpg' ) , ] ) ; // Optimized image component < Image source = { require ( './assets/photo.jpg' ) } contentFit = "cover" transition = { 200 } style = { styles . image } /

SQLite Database import * as SQLite from 'expo-sqlite' ; const db = await SQLite . openDatabaseAsync ( 'mydb.db' ) ; // Create table await db . execAsync ( CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE ); ) ; // Insert data await db . runAsync ( 'INSERT INTO users (name, email) VALUES (?, ?)' , 'John' , 'john@example.com' ) ; // Query data const users = await db . getAllAsync ( 'SELECT * FROM users' ) ; EAS Build and Submit eas.json Configuration { "cli" : { "version" : ">= 5.0.0" } , "build" : { "development" : { "developmentClient" : true , "distribution" : "internal" , "channel" : "development" , "ios" : { "simulator" : true } } , "preview" : { "distribution" : "internal" , "channel" : "preview" , "android" : { "buildType" : "apk" } } , "production" : { "channel" : "production" , "autoIncrement" : true , "env" : { "API_URL" : "https://api.production.com" } } } , "submit" : { "production" : { "ios" : { "ascAppId" : "1234567890" , "appleId" : "user@example.com" } , "android" : { "serviceAccountKeyPath" : "./google-service-account.json" , "track" : "production" } } } } Build Commands

Development build

eas build --profile development --platform ios

Preview build (internal testing)

eas build --profile preview --platform android

Production build

eas build --profile production --platform all

Build and auto-submit

eas build --profile production --auto-submit Build Environment Variables { "build" : { "production" : { "env" : { "API_URL" : "https://api.prod.com" , "SENTRY_DSN" : "https://..." } } } } Access in app: const apiUrl = process . env . EXPO_PUBLIC_API_URL ; Over-the-Air (OTA) Updates EAS Update Configuration { "expo" : { "runtimeVersion" : { "policy" : "appVersion" } , "updates" : { "url" : "https://u.expo.dev/[project-id]" } } } Publishing Updates

Publish to production channel

eas update --channel production --message "Fix login bug"

Publish to preview

eas update --channel preview --message "Test new feature"

View update history

eas update:list --channel production Update Channels Strategy // Different channels for different environments production -

main branch staging -

develop branch preview -

feature branches Checking for Updates in App import * as Updates from 'expo-updates' ; async function checkForUpdates ( ) { if ( ! DEV ) { const update = await Updates . checkForUpdateAsync ( ) ; if ( update . isAvailable ) { await Updates . fetchUpdateAsync ( ) ; await Updates . reloadAsync ( ) ; } } } // Check on app focus useEffect ( ( ) => { const subscription = AppState . addEventListener ( 'change' , state => { if ( state === 'active' ) { checkForUpdates ( ) ; } } ) ; return ( ) => subscription . remove ( ) ; } , [ ] ) ; Runtime Version Management { "expo" : { "runtimeVersion" : "1.0.0" } } Only compatible OTA updates will be delivered to builds with matching runtime versions. Native Module Integration Custom Native Modules with Expo Modules API // ios/MyModule.swift import ExpoModulesCore public class MyModule : Module { public func definition ( ) -

ModuleDefinition { Name ( "MyModule" ) Function ( "hello" ) { ( name : String ) -

String in return "Hello (name)!" } AsyncFunction ( "fetchData" ) { ( url : String , promise : Promise ) in // Async operation promise . resolve ( [ "data" : "value" ] ) } } } // Usage in JavaScript import { NativeModules } from 'react-native' ; const { MyModule } = NativeModules ; const greeting = MyModule . hello ( 'World' ) ; Config Plugins Create custom config plugin for native configuration: // app-plugin.js const { withAndroidManifest } = require ( '@expo/config-plugins' ) ; const withCustomManifest = config => { return withAndroidManifest ( config , async config => { const androidManifest = config . modResults ; // Modify manifest androidManifest . manifest . application [ 0 ] . $ [ 'android:usesCleartextTraffic' ] = 'true' ; return config ; } ) ; } ; module . exports = withCustomManifest ; Apply in app.json: { "expo" : { "plugins" : [ "./app-plugin.js" ] } } Using Third-Party Native Libraries Without Custom Native Code (Recommended): npx expo install react-native-reanimated With Custom Native Code: npx expo install react-native-camera npx expo prebuild App Configuration (app.json / app.config.js) Static Configuration (app.json) { "expo" : { "name" : "My App" , "slug" : "my-app" , "version" : "1.0.0" , "orientation" : "portrait" , "icon" : "./assets/icon.png" , "userInterfaceStyle" : "automatic" , "splash" : { "image" : "./assets/splash.png" , "resizeMode" : "contain" , "backgroundColor" : "#ffffff" } , "assetBundlePatterns" : [ "*/" ] , "ios" : { "supportsTablet" : true , "bundleIdentifier" : "com.company.myapp" , "buildNumber" : "1.0.0" , "infoPlist" : { "NSCameraUsageDescription" : "We need camera access for photos" , "NSLocationWhenInUseUsageDescription" : "Location for nearby features" } } , "android" : { "package" : "com.company.myapp" , "versionCode" : 1 , "adaptiveIcon" : { "foregroundImage" : "./assets/adaptive-icon.png" , "backgroundColor" : "#ffffff" } , "permissions" : [ "CAMERA" , "ACCESS_FINE_LOCATION" ] } , "web" : { "favicon" : "./assets/favicon.png" , "bundler" : "metro" } , "plugins" : [ "expo-router" , [ "expo-camera" , { "cameraPermission" : "Allow $(PRODUCT_NAME) to access camera" } ] ] , "extra" : { "apiUrl" : "https://api.example.com" } } } Dynamic Configuration (app.config.js) export default ( { config } ) => { const isProduction = process . env . APP_ENV === 'production' ; return { ... config , name : isProduction ? 'My App' : 'My App (Dev)' , slug : 'my-app' , extra : { apiUrl : isProduction ? 'https://api.production.com' : 'https://api.staging.com' , ... config . extra , } , ios : { ... config . ios , bundleIdentifier : isProduction ? 'com.company.myapp' : 'com.company.myapp.dev' , } , android : { ... config . android , package : isProduction ? 'com.company.myapp' : 'com.company.myapp.dev' , } , } ; } ; Environment-Specific Configuration // app.config.js const getEnvironment = ( ) => { if ( process . env . APP_ENV === 'production' ) { return { apiUrl : 'https://api.prod.com' , sentryDsn : 'https://prod-sentry-dsn' , } ; } return { apiUrl : 'https://api.dev.com' , sentryDsn : 'https://dev-sentry-dsn' , } ; } ; export default { expo : { extra : getEnvironment ( ) , } , } ; Access in app: import Constants from 'expo-constants' ; const apiUrl = Constants . expoConfig ?. extra ?. apiUrl ; Best Practices Performance Optimization Use expo-image instead of React Native Image for better performance Enable Hermes for Android: "jsEngine": "hermes" Use react-native-reanimated for smooth animations Lazy load screens with React.lazy() Code Splitting import { lazy , Suspense } from 'react' ; const ProfileScreen = lazy ( ( ) => import ( './screens/Profile' ) ) ; function App ( ) { return ( < Suspense fallback = { < LoadingScreen /

}

< ProfileScreen /

< / Suspense

) ; } Error Boundaries import * as Sentry from '@sentry/react-native' ; Sentry . init ( { dsn : 'your-sentry-dsn' , environment : DEV ? 'development' : 'production' , } ) ; export default Sentry . wrap ( App ) ; Expo Doctor Run before building: npx expo-doctor This checks for common issues with dependencies and configuration. Iron Laws ALWAYS use Expo Router for navigation — never use React Navigation directly in new Expo projects; Expo Router provides file-based routing that integrates with native navigation and deep linking. NEVER eject to bare workflow unless the required native module is genuinely unavailable through Expo SDK, config plugins, or EAS Build — ejecting increases maintenance burden by orders of magnitude. ALWAYS use EAS Build for production builds — never use local expo build (deprecated) or react-native run-ios/android for release builds; EAS ensures consistent reproducible builds. NEVER use expo-av for camera in new projects — use expo-camera directly; expo-av is audio/video focused and the camera integration is deprecated. ALWAYS use expo-image instead of React Native's built-in — expo-image provides lazy loading, caching, and blurhash placeholder support out of the box. Anti-Patterns Anti-Pattern Why It Fails Correct Approach Using React Navigation instead of Expo Router Misses file-based routing, native tab/stack integration, and automatic deep linking Use Expo Router; it wraps React Navigation with file-system conventions Ejecting early "just in case" Loses OTA updates, managed builds, and Expo SDK updates forever Exhaust all Expo SDK/config plugin options first; eject only as last resort Using deprecated expo build command Removed in SDK 46+; fails silently or produces broken artifacts Use eas build with a properly configured eas.json Direct require() for images in performance-critical code Large bundles; no lazy loading; no progressive blur placeholder Use expo-image with contentFit and blurhash for optimized loading Using expo-constants for secrets Constants are bundled into the app binary and visible in source maps Use EAS secrets or server-side environment variables; never bundle secrets Memory Protocol (MANDATORY) Before starting: cat .claude/context/memory/learnings.md After completing: Record any new patterns or exceptions discovered. ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.

返回排行榜