packwise-skills 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursorrules +23 -23
- package/CLAUDE.md +25 -25
- package/LICENSE +21 -0
- package/README.md +404 -295
- package/audit.md +224 -224
- package/bin/packwise.js +322 -155
- package/install.sh +123 -0
- package/package.json +32 -31
- package/skill.md +944 -719
- package/sub-skills/ai/local-llm.md +183 -183
- package/sub-skills/ai/python-ml.md +164 -164
- package/sub-skills/backend/go-server.md +184 -184
- package/sub-skills/backend/java-spring.md +241 -241
- package/sub-skills/backend/node-server.md +164 -164
- package/sub-skills/backend/php-laravel.md +175 -175
- package/sub-skills/backend/python-server.md +164 -164
- package/sub-skills/backend/rust-backend.md +118 -118
- package/sub-skills/cli/python-cli.md +236 -236
- package/sub-skills/cli/sdk-library.md +497 -497
- package/sub-skills/cloud/ci-cd-pipelines.md +350 -350
- package/sub-skills/cloud/docker.md +191 -191
- package/sub-skills/cloud/kubernetes.md +277 -277
- package/sub-skills/cloud/payment-integration.md +307 -307
- package/sub-skills/cross-platform/multiplatform.md +252 -252
- package/sub-skills/desktop/electron.md +783 -783
- package/sub-skills/desktop/game-dev.md +443 -443
- package/sub-skills/desktop/native-app.md +123 -123
- package/sub-skills/desktop/scenarios.md +443 -443
- package/sub-skills/desktop/smart-platforms.md +324 -324
- package/sub-skills/desktop/tauri.md +428 -428
- package/sub-skills/desktop/vr-ar.md +252 -252
- package/sub-skills/desktop/web-to-desktop.md +153 -153
- package/sub-skills/embedded/car-infotainment.md +129 -129
- package/sub-skills/embedded/esp32.md +184 -184
- package/sub-skills/embedded/ros.md +150 -150
- package/sub-skills/embedded/stm32.md +160 -160
- package/sub-skills/mobile/android.md +322 -322
- package/sub-skills/mobile/capacitor.md +232 -232
- package/sub-skills/mobile/flutter-mobile.md +138 -138
- package/sub-skills/mobile/harmonyos.md +150 -150
- package/sub-skills/mobile/ios.md +245 -245
- package/sub-skills/mobile/react-native.md +443 -443
- package/sub-skills/mobile/wearables.md +230 -230
- package/sub-skills/plugins/browser-extension.md +308 -308
- package/sub-skills/plugins/jetbrains-plugin.md +226 -226
- package/sub-skills/plugins/vscode-extension.md +204 -204
- package/sub-skills/security/security-tools.md +174 -174
- package/sub-skills/web/monorepo.md +274 -274
- package/sub-skills/web/pwa.md +220 -220
- package/sub-skills/web/serverless-edge.md +295 -295
- package/sub-skills/web/spa.md +266 -266
- package/sub-skills/web/ssr.md +228 -228
- package/sub-skills/web/wasm.md +243 -243
|
@@ -1,443 +1,443 @@
|
|
|
1
|
-
# React Native Build Sub-Skill
|
|
2
|
-
|
|
3
|
-
Build mobile apps using React Native with JavaScript or TypeScript.
|
|
4
|
-
|
|
5
|
-
**Current version**: React Native 0.86.x / Expo SDK 56 (2025-2026)
|
|
6
|
-
|
|
7
|
-
> ⚠️ **Critical breaking changes since RN 0.76**:
|
|
8
|
-
> - **Legacy architecture fully removed (v0.84)**: Old bridge modules, `NativeModules` direct access, `requireNativeComponent` all fail. ALL native modules must have TurboModule spec files.
|
|
9
|
-
> - **JSC engine removed (v0.81)**: Hermes is the sole JS engine. `hermesEnabled` property no longer meaningful.
|
|
10
|
-
> - **iOS minimum deployment target**: 13.4 (v0.76) → **16.0** (v0.78+). Apps targeting older iOS will be rejected.
|
|
11
|
-
> - **Deep imports removed (v0.82)**: `require('react-native/Libraries/...')` without `.default` breaks.
|
|
12
|
-
> - **Node.js 22.11+ required (v0.84)**.
|
|
13
|
-
> - **Expo SDK 56** required for RN 0.85/0.86 (was SDK 52 for RN 0.76).
|
|
14
|
-
> - **Metro package exports** enabled by default (v0.79) — may break Firebase, AWS Amplify.
|
|
15
|
-
> - **React 19**: `propTypes` removed, `ref` is regular prop, string refs removed.
|
|
16
|
-
> - **Jest preset moved (v0.85)**: `preset: 'react-native'` → `preset: '@react-native/jest-preset'`.
|
|
17
|
-
> - See [reactnative.dev/blog](https://reactnative.dev/blog) for version-specific guides.
|
|
18
|
-
|
|
19
|
-
## When to Use
|
|
20
|
-
|
|
21
|
-
- Team has React/JavaScript/TypeScript experience
|
|
22
|
-
- Need cross-platform app (iOS + Android) with near-native performance
|
|
23
|
-
- Want to leverage the React ecosystem and component model
|
|
24
|
-
|
|
25
|
-
## Key Features
|
|
26
|
-
|
|
27
|
-
- **Hermes engine** — default JS engine since RN 0.70, optimized for mobile (faster startup, lower memory)
|
|
28
|
-
- **Fabric renderer** — new architecture with synchronous native calls
|
|
29
|
-
- **TurboModules** — lazy-loaded native modules with type-safe JSI bindings
|
|
30
|
-
- **Expo ecosystem** — managed workflow for rapid development without Xcode/Android Studio setup
|
|
31
|
-
|
|
32
|
-
## Prerequisites
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# React Native CLI (bare workflow)
|
|
36
|
-
npm install -g react-native-cli
|
|
37
|
-
|
|
38
|
-
# Android
|
|
39
|
-
# Android Studio, JDK 17, Android SDK 34+
|
|
40
|
-
|
|
41
|
-
# iOS (macOS only)
|
|
42
|
-
# Xcode 15+, CocoaPods (sudo gem install cocoapods)
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Two Workflows: Expo vs Bare
|
|
46
|
-
|
|
47
|
-
| Feature | Expo (Managed) | Bare Workflow |
|
|
48
|
-
|---------|---------------|---------------|
|
|
49
|
-
| Setup | `npx create-expo-app` | `npx react-native init` |
|
|
50
|
-
| Native code | No direct access | Full control |
|
|
51
|
-
| Build service | EAS Cloud Build | Local + CI |
|
|
52
|
-
| OTA updates | Yes (EAS Update) | Manual |
|
|
53
|
-
| Best for | New projects, rapid prototyping | Existing native modules, full control |
|
|
54
|
-
|
|
55
|
-
## Expo Managed Workflow (Recommended for New Projects)
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
# Create project
|
|
59
|
-
npx create-expo-app@latest MyApp --template blank-typescript
|
|
60
|
-
cd MyApp
|
|
61
|
-
|
|
62
|
-
# Local development
|
|
63
|
-
npx expo start # Start dev server
|
|
64
|
-
npx expo start --ios # Run on iOS simulator
|
|
65
|
-
npx expo start --android # Run on Android emulator
|
|
66
|
-
|
|
67
|
-
# Export for production
|
|
68
|
-
npx expo export --platform all
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### EAS Build (Cloud Build Service)
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
# Install EAS CLI
|
|
75
|
-
npm install -g eas-cli
|
|
76
|
-
eas login
|
|
77
|
-
|
|
78
|
-
# Configure build profiles
|
|
79
|
-
# Creates eas.json with development/preview/production profiles
|
|
80
|
-
eas build:configure
|
|
81
|
-
|
|
82
|
-
# Build for Android (AAB for Play Store) — requires Node 22.11+ for RN 0.84+
|
|
83
|
-
eas build --platform android --profile production
|
|
84
|
-
|
|
85
|
-
# Build for iOS (IPA for App Store) — minimum iOS 16.0 for RN 0.78+
|
|
86
|
-
eas build --platform ios --profile production
|
|
87
|
-
|
|
88
|
-
# Build for both platforms simultaneously
|
|
89
|
-
eas build --platform all --profile production
|
|
90
|
-
|
|
91
|
-
# Local build (no cloud credits needed)
|
|
92
|
-
eas build --platform android --profile production --local
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
```json
|
|
96
|
-
// eas.json
|
|
97
|
-
{
|
|
98
|
-
"cli": { "version": ">= 12.0.0" },
|
|
99
|
-
"build": {
|
|
100
|
-
"development": {
|
|
101
|
-
"developmentClient": true,
|
|
102
|
-
"distribution": "internal"
|
|
103
|
-
},
|
|
104
|
-
"preview": {
|
|
105
|
-
"distribution": "internal",
|
|
106
|
-
"android": { "buildType": "apk" }
|
|
107
|
-
},
|
|
108
|
-
"production": {
|
|
109
|
-
"android": {
|
|
110
|
-
"buildType": "app-bundle"
|
|
111
|
-
},
|
|
112
|
-
"ios": {
|
|
113
|
-
"autoIncrement": true
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
"submit": {
|
|
118
|
-
"production": {
|
|
119
|
-
"ios": {
|
|
120
|
-
"appleId": "your-apple-id@example.com",
|
|
121
|
-
"ascAppId": "your-app-store-connect-app-id"
|
|
122
|
-
},
|
|
123
|
-
"android": {
|
|
124
|
-
"serviceAccountKeyPath": "./google-services.json",
|
|
125
|
-
"track": "production"
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### EAS Update (OTA Updates)
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
# Push OTA update (no app store review needed)
|
|
136
|
-
eas update --branch production --message "Fix login bug"
|
|
137
|
-
|
|
138
|
-
# Rollback to previous version
|
|
139
|
-
eas update --branch production --message "Rollback" --republish
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Bare Workflow (Full Native Control)
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
# Create project
|
|
146
|
-
npx @react-native-community/cli init MyApp --version 0.76
|
|
147
|
-
cd MyApp
|
|
148
|
-
|
|
149
|
-
# Verify setup
|
|
150
|
-
npx react-native doctor
|
|
151
|
-
|
|
152
|
-
# Run
|
|
153
|
-
npx react-native run-android
|
|
154
|
-
npx react-native run-ios
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Hermes Engine (Default)
|
|
158
|
-
|
|
159
|
-
Hermes is the default JS engine since RN 0.70. Verify it's enabled:
|
|
160
|
-
|
|
161
|
-
```javascript
|
|
162
|
-
// android/gradle.properties
|
|
163
|
-
hermesEnabled=true // should be true by default
|
|
164
|
-
|
|
165
|
-
// ios/Podfile (auto-configured)
|
|
166
|
-
:hermes_enabled => true
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
# Verify Hermes is active at runtime
|
|
171
|
-
console.log('Hermes:', global.HermesInternal != null); // should print true
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Android Release Build
|
|
175
|
-
|
|
176
|
-
```bash
|
|
177
|
-
# Generate signing keystore
|
|
178
|
-
keytool -genkeypair -v -storetype PKCS12 \
|
|
179
|
-
-keystore android/app/my-release-key.keystore \
|
|
180
|
-
-alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
|
|
181
|
-
|
|
182
|
-
# Set signing config in android/app/build.gradle.kts
|
|
183
|
-
# OR use environment variables (recommended for CI):
|
|
184
|
-
export MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
|
|
185
|
-
export MYAPP_RELEASE_KEY_ALIAS=my-key-alias
|
|
186
|
-
export MYAPP_RELEASE_STORE_PASSWORD=your-password
|
|
187
|
-
export MYAPP_RELEASE_KEY_PASSWORD=your-password
|
|
188
|
-
|
|
189
|
-
# Build AAB (Play Store)
|
|
190
|
-
cd android && ./gradlew bundleRelease
|
|
191
|
-
# Output: android/app/build/outputs/bundle/release/app-release.aab
|
|
192
|
-
|
|
193
|
-
# Build APK (direct distribution)
|
|
194
|
-
cd android && ./gradlew assembleRelease
|
|
195
|
-
# Output: android/app/build/outputs/apk/release/app-release.apk
|
|
196
|
-
|
|
197
|
-
# Run release build on device
|
|
198
|
-
npx react-native run-android --mode=release
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### iOS Release Build
|
|
202
|
-
|
|
203
|
-
```bash
|
|
204
|
-
# Install pods
|
|
205
|
-
cd ios && pod install && cd ..
|
|
206
|
-
|
|
207
|
-
# Build via Xcode
|
|
208
|
-
open ios/MyApp.xcworkspace
|
|
209
|
-
# Product → Archive → Distribute App
|
|
210
|
-
|
|
211
|
-
# Build via command line (requires fastlane)
|
|
212
|
-
sudo gem install fastlane
|
|
213
|
-
cd ios && fastlane release
|
|
214
|
-
|
|
215
|
-
# TestFlight upload
|
|
216
|
-
cd ios && fastlane beta
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
## React Native vs Flutter
|
|
220
|
-
|
|
221
|
-
| Feature | React Native | Flutter |
|
|
222
|
-
|---------|-------------|---------|
|
|
223
|
-
| Language | JavaScript/TypeScript | Dart |
|
|
224
|
-
| UI | Native components | Custom rendering (Skia/Impeller) |
|
|
225
|
-
| Hot reload | Fast Refresh | Hot reload |
|
|
226
|
-
| Performance | Near-native (Hermes + Fabric) | Near-native (AOT compilation) |
|
|
227
|
-
| Ecosystem | Huge (npm) | Growing (pub.dev) |
|
|
228
|
-
| Web support | Via react-native-web | First-class |
|
|
229
|
-
| Learning curve | Low (if you know React) | Medium (Dart is less common) |
|
|
230
|
-
|
|
231
|
-
## Common Pitfalls
|
|
232
|
-
|
|
233
|
-
| Issue | Fix |
|
|
234
|
-
|-------|-----|
|
|
235
|
-
| Metro bundler cache issues | `npx react-native start --reset-cache` |
|
|
236
|
-
| iOS build fails after pod install | `cd ios && pod deinstall && pod install`; check Ruby version (3.1+) |
|
|
237
|
-
| Android SDK not found | Set `ANDROID_HOME` env variable; verify with `npx react-native doctor` |
|
|
238
|
-
| Hermes not enabled | Check `hermesEnabled=true` in `android/gradle.properties`; run `pod install` |
|
|
239
|
-
| Release build crashes but debug works | Check ProGuard rules; add keep rules for native modules |
|
|
240
|
-
| App size too large | Enable Hermes; use ProGuard; split APKs by ABI |
|
|
241
|
-
| Network request fails in release | iOS: add App Transport Security exception in Info.plist |
|
|
242
|
-
| `react-native-reanimated` crash | Ensure Babel plugin is in `babel.config.js` |
|
|
243
|
-
| EAS build timeout | Use `--local` flag for local builds; check build logs for specific errors |
|
|
244
|
-
| New Architecture compatibility | Check library support at reactnative.directory; many libs still on old arch |
|
|
245
|
-
| "Namespace not specified" error | AGP 8.x requires `namespace` in `android/app/build.gradle`; remove `package=` from AndroidManifest.xml |
|
|
246
|
-
| Legacy arch modules crash (0.84+) | Old bridge modules fully removed; must use TurboModule spec files |
|
|
247
|
-
| JSC not available (0.81+) | Hermes is sole engine; `hermesEnabled` toggle no longer meaningful |
|
|
248
|
-
| iOS build rejected (0.78+) | Minimum iOS deployment target is now 16.0 (was 13.4) |
|
|
249
|
-
| Deep import fails (0.82+) | `require('react-native/Libraries/...')` removed; use top-level exports |
|
|
250
|
-
| Metro package exports break (0.79+) | Add `resolver.unstable_enablePackageExports: false` to metro.config.js to opt out |
|
|
251
|
-
| Jest preset not found (0.85+) | Change `preset: 'react-native'` to `preset: '@react-native/jest-preset'` |
|
|
252
|
-
| Firebase/Amplify build fails (0.79+) | Metro exports resolution enabled by default; see Metro config fix above |
|
|
253
|
-
| App Store rejected: missing Privacy Manifest | Add `PrivacyInfo.xcprivacy` to iOS project (see below) |
|
|
254
|
-
| `ClassNotFoundException` on release | Missing ProGuard rules — see ProGuard section below |
|
|
255
|
-
|
|
256
|
-
## Critical: AGP 8.x Namespace Requirement
|
|
257
|
-
|
|
258
|
-
AGP 8.0+ removed `package` attribute from `AndroidManifest.xml`. You MUST use `namespace` in `build.gradle`:
|
|
259
|
-
|
|
260
|
-
```gradle
|
|
261
|
-
// android/app/build.gradle
|
|
262
|
-
android {
|
|
263
|
-
namespace "com.yourapp" // REQUIRED in AGP 8.x
|
|
264
|
-
compileSdk 35
|
|
265
|
-
defaultConfig {
|
|
266
|
-
applicationId "com.yourapp"
|
|
267
|
-
minSdkVersion 24
|
|
268
|
-
targetSdkVersion 35
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
```xml
|
|
274
|
-
<!-- AndroidManifest.xml — REMOVE package attribute -->
|
|
275
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
276
|
-
<!-- no package="..." attribute -->
|
|
277
|
-
</manifest>
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## New Architecture (Mandatory since RN 0.84)
|
|
281
|
-
|
|
282
|
-
Fabric renderer and TurboModules are **mandatory** since React Native 0.84. Legacy architecture code has been completely removed. There is no opt-out.
|
|
283
|
-
|
|
284
|
-
```properties
|
|
285
|
-
# android/gradle.properties (RN 0.84+)
|
|
286
|
-
# newArchEnabled is no longer configurable — it's always on
|
|
287
|
-
hermesEnabled=true # Hermes is the sole engine (JSC removed in 0.81)
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
**What this means:**
|
|
291
|
-
- ALL native modules must have TurboModule spec files
|
|
292
|
-
- Libraries using old `UIManager` bridge calls will NOT work
|
|
293
|
-
- `NativeModules` direct access deprecated — use TurboModule specs
|
|
294
|
-
- `requireNativeComponent` replaced by Fabric component specs
|
|
295
|
-
- Check library compatibility: [reactnative.directory](https://reactnative.directory)
|
|
296
|
-
|
|
297
|
-
## ProGuard Rules (Required for Release Builds)
|
|
298
|
-
|
|
299
|
-
> ⚠️ **R8 full mode is now default** for release builds (more aggressive shrinking). Without these rules, your release build WILL crash at runtime. Legacy architecture classes removed in RN 0.84+ may require updated rules.
|
|
300
|
-
|
|
301
|
-
```proguard
|
|
302
|
-
# android/app/proguard-rules.pro
|
|
303
|
-
|
|
304
|
-
# React Native Core
|
|
305
|
-
-keep class com.facebook.react.** { *; }
|
|
306
|
-
-keep class com.facebook.hermes.** { *; }
|
|
307
|
-
-keep class com.facebook.jni.** { *; }
|
|
308
|
-
|
|
309
|
-
# Hermes
|
|
310
|
-
-keep class com.facebook.hermes.unicode.** { *; }
|
|
311
|
-
|
|
312
|
-
# Native Modules (CRITICAL — R8 strips these without keep rules)
|
|
313
|
-
-keepnames class * extends com.facebook.react.bridge.NativeModule
|
|
314
|
-
-keepnames class * extends com.facebook.react.TurboModule
|
|
315
|
-
-keepclassmembers class * extends com.facebook.react.bridge.NativeModule {
|
|
316
|
-
@com.facebook.react.bridge.ReactMethod <methods>;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
# Views
|
|
320
|
-
-keep class com.facebook.react.views.** { *; }
|
|
321
|
-
-keep class com.facebook.react.uimanager.** { *; }
|
|
322
|
-
-keep class com.facebook.react.animated.** { *; }
|
|
323
|
-
|
|
324
|
-
# Common third-party libraries
|
|
325
|
-
-keep class com.swmansion.gesturehandler.** { *; }
|
|
326
|
-
-keep class com.swmansion.reanimated.** { *; }
|
|
327
|
-
-keep class com.swmansion.rnscreens.** { *; }
|
|
328
|
-
-keep class com.reactnativecommunity.asyncstorage.** { *; }
|
|
329
|
-
-keep class com.horcrux.svg.** { *; }
|
|
330
|
-
|
|
331
|
-
# Expo modules (if using Expo)
|
|
332
|
-
-keep class expo.modules.** { *; }
|
|
333
|
-
|
|
334
|
-
# OkHttp
|
|
335
|
-
-dontwarn okhttp3.**
|
|
336
|
-
-keep class okhttp3.** { *; }
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
Enable in `android/app/build.gradle`:
|
|
340
|
-
|
|
341
|
-
```gradle
|
|
342
|
-
android {
|
|
343
|
-
buildTypes {
|
|
344
|
-
release {
|
|
345
|
-
minifyEnabled true
|
|
346
|
-
shrinkResources true
|
|
347
|
-
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## iOS Privacy Manifest (Required for App Store)
|
|
354
|
-
|
|
355
|
-
Apple requires `PrivacyInfo.xcprivacy` since Spring 2024. Without it, your app will be rejected.
|
|
356
|
-
|
|
357
|
-
```xml
|
|
358
|
-
<!-- ios/YourApp/PrivacyInfo.xcprivacy -->
|
|
359
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
360
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
361
|
-
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
362
|
-
<plist version="1.0">
|
|
363
|
-
<dict>
|
|
364
|
-
<key>NSPrivacyTracking</key>
|
|
365
|
-
<false/>
|
|
366
|
-
<key>NSPrivacyTrackingDomains</key>
|
|
367
|
-
<array/>
|
|
368
|
-
<key>NSPrivacyCollectedDataTypes</key>
|
|
369
|
-
<array/>
|
|
370
|
-
<key>NSPrivacyAccessedAPITypes</key>
|
|
371
|
-
<array>
|
|
372
|
-
<dict>
|
|
373
|
-
<key>NSPrivacyAccessedAPIType</key>
|
|
374
|
-
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
375
|
-
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
376
|
-
<array><string>CA92.1</string></array>
|
|
377
|
-
</dict>
|
|
378
|
-
<dict>
|
|
379
|
-
<key>NSPrivacyAccessedAPIType</key>
|
|
380
|
-
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
381
|
-
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
382
|
-
<array><string>C617.1</string></array>
|
|
383
|
-
</dict>
|
|
384
|
-
<dict>
|
|
385
|
-
<key>NSPrivacyAccessedAPIType</key>
|
|
386
|
-
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
|
387
|
-
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
388
|
-
<array><string>35F9.1</string></array>
|
|
389
|
-
</dict>
|
|
390
|
-
</array>
|
|
391
|
-
</dict>
|
|
392
|
-
</plist>
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
Add to Xcode: Drag into app group → ensure "Copy Bundle Resources" includes it.
|
|
396
|
-
|
|
397
|
-
## Secure Signing Configuration
|
|
398
|
-
|
|
399
|
-
Never commit passwords to version control.
|
|
400
|
-
|
|
401
|
-
```gradle
|
|
402
|
-
// android/app/build.gradle — secure signing
|
|
403
|
-
signingConfigs {
|
|
404
|
-
release {
|
|
405
|
-
storeFile file(MYAPP_RELEASE_STORE_FILE ?: System.getenv("MYAPP_RELEASE_STORE_FILE") ?: "release.keystore")
|
|
406
|
-
storePassword MYAPP_RELEASE_STORE_PASSWORD ?: System.getenv("MYAPP_RELEASE_STORE_PASSWORD")
|
|
407
|
-
keyAlias MYAPP_RELEASE_KEY_ALIAS ?: System.getenv("MYAPP_RELEASE_KEY_ALIAS")
|
|
408
|
-
keyPassword MYAPP_RELEASE_KEY_PASSWORD ?: System.getenv("MYAPP_RELEASE_KEY_PASSWORD")
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
```yaml
|
|
414
|
-
# GitHub Actions — decode keystore from secrets
|
|
415
|
-
- name: Decode Keystore
|
|
416
|
-
run: echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 --decode > android/app/release.keystore
|
|
417
|
-
- name: Build Release
|
|
418
|
-
env:
|
|
419
|
-
MYAPP_RELEASE_STORE_FILE: release.keystore
|
|
420
|
-
MYAPP_RELEASE_STORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
421
|
-
MYAPP_RELEASE_KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
422
|
-
MYAPP_RELEASE_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
423
|
-
run: cd android && ./gradlew bundleRelease
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
## Metro Production Configuration
|
|
427
|
-
|
|
428
|
-
```javascript
|
|
429
|
-
// metro.config.js
|
|
430
|
-
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
|
431
|
-
|
|
432
|
-
const config = {
|
|
433
|
-
transformer: {
|
|
434
|
-
minifierConfig: {
|
|
435
|
-
compress: {
|
|
436
|
-
drop_console: true, // Remove console.log in production
|
|
437
|
-
},
|
|
438
|
-
},
|
|
439
|
-
},
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
|
443
|
-
```
|
|
1
|
+
# React Native Build Sub-Skill
|
|
2
|
+
|
|
3
|
+
Build mobile apps using React Native with JavaScript or TypeScript.
|
|
4
|
+
|
|
5
|
+
**Current version**: React Native 0.86.x / Expo SDK 56 (2025-2026)
|
|
6
|
+
|
|
7
|
+
> ⚠️ **Critical breaking changes since RN 0.76**:
|
|
8
|
+
> - **Legacy architecture fully removed (v0.84)**: Old bridge modules, `NativeModules` direct access, `requireNativeComponent` all fail. ALL native modules must have TurboModule spec files.
|
|
9
|
+
> - **JSC engine removed (v0.81)**: Hermes is the sole JS engine. `hermesEnabled` property no longer meaningful.
|
|
10
|
+
> - **iOS minimum deployment target**: 13.4 (v0.76) → **16.0** (v0.78+). Apps targeting older iOS will be rejected.
|
|
11
|
+
> - **Deep imports removed (v0.82)**: `require('react-native/Libraries/...')` without `.default` breaks.
|
|
12
|
+
> - **Node.js 22.11+ required (v0.84)**.
|
|
13
|
+
> - **Expo SDK 56** required for RN 0.85/0.86 (was SDK 52 for RN 0.76).
|
|
14
|
+
> - **Metro package exports** enabled by default (v0.79) — may break Firebase, AWS Amplify.
|
|
15
|
+
> - **React 19**: `propTypes` removed, `ref` is regular prop, string refs removed.
|
|
16
|
+
> - **Jest preset moved (v0.85)**: `preset: 'react-native'` → `preset: '@react-native/jest-preset'`.
|
|
17
|
+
> - See [reactnative.dev/blog](https://reactnative.dev/blog) for version-specific guides.
|
|
18
|
+
|
|
19
|
+
## When to Use
|
|
20
|
+
|
|
21
|
+
- Team has React/JavaScript/TypeScript experience
|
|
22
|
+
- Need cross-platform app (iOS + Android) with near-native performance
|
|
23
|
+
- Want to leverage the React ecosystem and component model
|
|
24
|
+
|
|
25
|
+
## Key Features
|
|
26
|
+
|
|
27
|
+
- **Hermes engine** — default JS engine since RN 0.70, optimized for mobile (faster startup, lower memory)
|
|
28
|
+
- **Fabric renderer** — new architecture with synchronous native calls
|
|
29
|
+
- **TurboModules** — lazy-loaded native modules with type-safe JSI bindings
|
|
30
|
+
- **Expo ecosystem** — managed workflow for rapid development without Xcode/Android Studio setup
|
|
31
|
+
|
|
32
|
+
## Prerequisites
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# React Native CLI (bare workflow)
|
|
36
|
+
npm install -g react-native-cli
|
|
37
|
+
|
|
38
|
+
# Android
|
|
39
|
+
# Android Studio, JDK 17, Android SDK 34+
|
|
40
|
+
|
|
41
|
+
# iOS (macOS only)
|
|
42
|
+
# Xcode 15+, CocoaPods (sudo gem install cocoapods)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Two Workflows: Expo vs Bare
|
|
46
|
+
|
|
47
|
+
| Feature | Expo (Managed) | Bare Workflow |
|
|
48
|
+
|---------|---------------|---------------|
|
|
49
|
+
| Setup | `npx create-expo-app` | `npx react-native init` |
|
|
50
|
+
| Native code | No direct access | Full control |
|
|
51
|
+
| Build service | EAS Cloud Build | Local + CI |
|
|
52
|
+
| OTA updates | Yes (EAS Update) | Manual |
|
|
53
|
+
| Best for | New projects, rapid prototyping | Existing native modules, full control |
|
|
54
|
+
|
|
55
|
+
## Expo Managed Workflow (Recommended for New Projects)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Create project
|
|
59
|
+
npx create-expo-app@latest MyApp --template blank-typescript
|
|
60
|
+
cd MyApp
|
|
61
|
+
|
|
62
|
+
# Local development
|
|
63
|
+
npx expo start # Start dev server
|
|
64
|
+
npx expo start --ios # Run on iOS simulator
|
|
65
|
+
npx expo start --android # Run on Android emulator
|
|
66
|
+
|
|
67
|
+
# Export for production
|
|
68
|
+
npx expo export --platform all
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### EAS Build (Cloud Build Service)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Install EAS CLI
|
|
75
|
+
npm install -g eas-cli
|
|
76
|
+
eas login
|
|
77
|
+
|
|
78
|
+
# Configure build profiles
|
|
79
|
+
# Creates eas.json with development/preview/production profiles
|
|
80
|
+
eas build:configure
|
|
81
|
+
|
|
82
|
+
# Build for Android (AAB for Play Store) — requires Node 22.11+ for RN 0.84+
|
|
83
|
+
eas build --platform android --profile production
|
|
84
|
+
|
|
85
|
+
# Build for iOS (IPA for App Store) — minimum iOS 16.0 for RN 0.78+
|
|
86
|
+
eas build --platform ios --profile production
|
|
87
|
+
|
|
88
|
+
# Build for both platforms simultaneously
|
|
89
|
+
eas build --platform all --profile production
|
|
90
|
+
|
|
91
|
+
# Local build (no cloud credits needed)
|
|
92
|
+
eas build --platform android --profile production --local
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
// eas.json
|
|
97
|
+
{
|
|
98
|
+
"cli": { "version": ">= 12.0.0" },
|
|
99
|
+
"build": {
|
|
100
|
+
"development": {
|
|
101
|
+
"developmentClient": true,
|
|
102
|
+
"distribution": "internal"
|
|
103
|
+
},
|
|
104
|
+
"preview": {
|
|
105
|
+
"distribution": "internal",
|
|
106
|
+
"android": { "buildType": "apk" }
|
|
107
|
+
},
|
|
108
|
+
"production": {
|
|
109
|
+
"android": {
|
|
110
|
+
"buildType": "app-bundle"
|
|
111
|
+
},
|
|
112
|
+
"ios": {
|
|
113
|
+
"autoIncrement": true
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"submit": {
|
|
118
|
+
"production": {
|
|
119
|
+
"ios": {
|
|
120
|
+
"appleId": "your-apple-id@example.com",
|
|
121
|
+
"ascAppId": "your-app-store-connect-app-id"
|
|
122
|
+
},
|
|
123
|
+
"android": {
|
|
124
|
+
"serviceAccountKeyPath": "./google-services.json",
|
|
125
|
+
"track": "production"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### EAS Update (OTA Updates)
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Push OTA update (no app store review needed)
|
|
136
|
+
eas update --branch production --message "Fix login bug"
|
|
137
|
+
|
|
138
|
+
# Rollback to previous version
|
|
139
|
+
eas update --branch production --message "Rollback" --republish
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Bare Workflow (Full Native Control)
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Create project
|
|
146
|
+
npx @react-native-community/cli init MyApp --version 0.76
|
|
147
|
+
cd MyApp
|
|
148
|
+
|
|
149
|
+
# Verify setup
|
|
150
|
+
npx react-native doctor
|
|
151
|
+
|
|
152
|
+
# Run
|
|
153
|
+
npx react-native run-android
|
|
154
|
+
npx react-native run-ios
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Hermes Engine (Default)
|
|
158
|
+
|
|
159
|
+
Hermes is the default JS engine since RN 0.70. Verify it's enabled:
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
// android/gradle.properties
|
|
163
|
+
hermesEnabled=true // should be true by default
|
|
164
|
+
|
|
165
|
+
// ios/Podfile (auto-configured)
|
|
166
|
+
:hermes_enabled => true
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Verify Hermes is active at runtime
|
|
171
|
+
console.log('Hermes:', global.HermesInternal != null); // should print true
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Android Release Build
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Generate signing keystore
|
|
178
|
+
keytool -genkeypair -v -storetype PKCS12 \
|
|
179
|
+
-keystore android/app/my-release-key.keystore \
|
|
180
|
+
-alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
|
|
181
|
+
|
|
182
|
+
# Set signing config in android/app/build.gradle.kts
|
|
183
|
+
# OR use environment variables (recommended for CI):
|
|
184
|
+
export MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
|
|
185
|
+
export MYAPP_RELEASE_KEY_ALIAS=my-key-alias
|
|
186
|
+
export MYAPP_RELEASE_STORE_PASSWORD=your-password
|
|
187
|
+
export MYAPP_RELEASE_KEY_PASSWORD=your-password
|
|
188
|
+
|
|
189
|
+
# Build AAB (Play Store)
|
|
190
|
+
cd android && ./gradlew bundleRelease
|
|
191
|
+
# Output: android/app/build/outputs/bundle/release/app-release.aab
|
|
192
|
+
|
|
193
|
+
# Build APK (direct distribution)
|
|
194
|
+
cd android && ./gradlew assembleRelease
|
|
195
|
+
# Output: android/app/build/outputs/apk/release/app-release.apk
|
|
196
|
+
|
|
197
|
+
# Run release build on device
|
|
198
|
+
npx react-native run-android --mode=release
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### iOS Release Build
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Install pods
|
|
205
|
+
cd ios && pod install && cd ..
|
|
206
|
+
|
|
207
|
+
# Build via Xcode
|
|
208
|
+
open ios/MyApp.xcworkspace
|
|
209
|
+
# Product → Archive → Distribute App
|
|
210
|
+
|
|
211
|
+
# Build via command line (requires fastlane)
|
|
212
|
+
sudo gem install fastlane
|
|
213
|
+
cd ios && fastlane release
|
|
214
|
+
|
|
215
|
+
# TestFlight upload
|
|
216
|
+
cd ios && fastlane beta
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## React Native vs Flutter
|
|
220
|
+
|
|
221
|
+
| Feature | React Native | Flutter |
|
|
222
|
+
|---------|-------------|---------|
|
|
223
|
+
| Language | JavaScript/TypeScript | Dart |
|
|
224
|
+
| UI | Native components | Custom rendering (Skia/Impeller) |
|
|
225
|
+
| Hot reload | Fast Refresh | Hot reload |
|
|
226
|
+
| Performance | Near-native (Hermes + Fabric) | Near-native (AOT compilation) |
|
|
227
|
+
| Ecosystem | Huge (npm) | Growing (pub.dev) |
|
|
228
|
+
| Web support | Via react-native-web | First-class |
|
|
229
|
+
| Learning curve | Low (if you know React) | Medium (Dart is less common) |
|
|
230
|
+
|
|
231
|
+
## Common Pitfalls
|
|
232
|
+
|
|
233
|
+
| Issue | Fix |
|
|
234
|
+
|-------|-----|
|
|
235
|
+
| Metro bundler cache issues | `npx react-native start --reset-cache` |
|
|
236
|
+
| iOS build fails after pod install | `cd ios && pod deinstall && pod install`; check Ruby version (3.1+) |
|
|
237
|
+
| Android SDK not found | Set `ANDROID_HOME` env variable; verify with `npx react-native doctor` |
|
|
238
|
+
| Hermes not enabled | Check `hermesEnabled=true` in `android/gradle.properties`; run `pod install` |
|
|
239
|
+
| Release build crashes but debug works | Check ProGuard rules; add keep rules for native modules |
|
|
240
|
+
| App size too large | Enable Hermes; use ProGuard; split APKs by ABI |
|
|
241
|
+
| Network request fails in release | iOS: add App Transport Security exception in Info.plist |
|
|
242
|
+
| `react-native-reanimated` crash | Ensure Babel plugin is in `babel.config.js` |
|
|
243
|
+
| EAS build timeout | Use `--local` flag for local builds; check build logs for specific errors |
|
|
244
|
+
| New Architecture compatibility | Check library support at reactnative.directory; many libs still on old arch |
|
|
245
|
+
| "Namespace not specified" error | AGP 8.x requires `namespace` in `android/app/build.gradle`; remove `package=` from AndroidManifest.xml |
|
|
246
|
+
| Legacy arch modules crash (0.84+) | Old bridge modules fully removed; must use TurboModule spec files |
|
|
247
|
+
| JSC not available (0.81+) | Hermes is sole engine; `hermesEnabled` toggle no longer meaningful |
|
|
248
|
+
| iOS build rejected (0.78+) | Minimum iOS deployment target is now 16.0 (was 13.4) |
|
|
249
|
+
| Deep import fails (0.82+) | `require('react-native/Libraries/...')` removed; use top-level exports |
|
|
250
|
+
| Metro package exports break (0.79+) | Add `resolver.unstable_enablePackageExports: false` to metro.config.js to opt out |
|
|
251
|
+
| Jest preset not found (0.85+) | Change `preset: 'react-native'` to `preset: '@react-native/jest-preset'` |
|
|
252
|
+
| Firebase/Amplify build fails (0.79+) | Metro exports resolution enabled by default; see Metro config fix above |
|
|
253
|
+
| App Store rejected: missing Privacy Manifest | Add `PrivacyInfo.xcprivacy` to iOS project (see below) |
|
|
254
|
+
| `ClassNotFoundException` on release | Missing ProGuard rules — see ProGuard section below |
|
|
255
|
+
|
|
256
|
+
## Critical: AGP 8.x Namespace Requirement
|
|
257
|
+
|
|
258
|
+
AGP 8.0+ removed `package` attribute from `AndroidManifest.xml`. You MUST use `namespace` in `build.gradle`:
|
|
259
|
+
|
|
260
|
+
```gradle
|
|
261
|
+
// android/app/build.gradle
|
|
262
|
+
android {
|
|
263
|
+
namespace "com.yourapp" // REQUIRED in AGP 8.x
|
|
264
|
+
compileSdk 35
|
|
265
|
+
defaultConfig {
|
|
266
|
+
applicationId "com.yourapp"
|
|
267
|
+
minSdkVersion 24
|
|
268
|
+
targetSdkVersion 35
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```xml
|
|
274
|
+
<!-- AndroidManifest.xml — REMOVE package attribute -->
|
|
275
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
276
|
+
<!-- no package="..." attribute -->
|
|
277
|
+
</manifest>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## New Architecture (Mandatory since RN 0.84)
|
|
281
|
+
|
|
282
|
+
Fabric renderer and TurboModules are **mandatory** since React Native 0.84. Legacy architecture code has been completely removed. There is no opt-out.
|
|
283
|
+
|
|
284
|
+
```properties
|
|
285
|
+
# android/gradle.properties (RN 0.84+)
|
|
286
|
+
# newArchEnabled is no longer configurable — it's always on
|
|
287
|
+
hermesEnabled=true # Hermes is the sole engine (JSC removed in 0.81)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**What this means:**
|
|
291
|
+
- ALL native modules must have TurboModule spec files
|
|
292
|
+
- Libraries using old `UIManager` bridge calls will NOT work
|
|
293
|
+
- `NativeModules` direct access deprecated — use TurboModule specs
|
|
294
|
+
- `requireNativeComponent` replaced by Fabric component specs
|
|
295
|
+
- Check library compatibility: [reactnative.directory](https://reactnative.directory)
|
|
296
|
+
|
|
297
|
+
## ProGuard Rules (Required for Release Builds)
|
|
298
|
+
|
|
299
|
+
> ⚠️ **R8 full mode is now default** for release builds (more aggressive shrinking). Without these rules, your release build WILL crash at runtime. Legacy architecture classes removed in RN 0.84+ may require updated rules.
|
|
300
|
+
|
|
301
|
+
```proguard
|
|
302
|
+
# android/app/proguard-rules.pro
|
|
303
|
+
|
|
304
|
+
# React Native Core
|
|
305
|
+
-keep class com.facebook.react.** { *; }
|
|
306
|
+
-keep class com.facebook.hermes.** { *; }
|
|
307
|
+
-keep class com.facebook.jni.** { *; }
|
|
308
|
+
|
|
309
|
+
# Hermes
|
|
310
|
+
-keep class com.facebook.hermes.unicode.** { *; }
|
|
311
|
+
|
|
312
|
+
# Native Modules (CRITICAL — R8 strips these without keep rules)
|
|
313
|
+
-keepnames class * extends com.facebook.react.bridge.NativeModule
|
|
314
|
+
-keepnames class * extends com.facebook.react.TurboModule
|
|
315
|
+
-keepclassmembers class * extends com.facebook.react.bridge.NativeModule {
|
|
316
|
+
@com.facebook.react.bridge.ReactMethod <methods>;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
# Views
|
|
320
|
+
-keep class com.facebook.react.views.** { *; }
|
|
321
|
+
-keep class com.facebook.react.uimanager.** { *; }
|
|
322
|
+
-keep class com.facebook.react.animated.** { *; }
|
|
323
|
+
|
|
324
|
+
# Common third-party libraries
|
|
325
|
+
-keep class com.swmansion.gesturehandler.** { *; }
|
|
326
|
+
-keep class com.swmansion.reanimated.** { *; }
|
|
327
|
+
-keep class com.swmansion.rnscreens.** { *; }
|
|
328
|
+
-keep class com.reactnativecommunity.asyncstorage.** { *; }
|
|
329
|
+
-keep class com.horcrux.svg.** { *; }
|
|
330
|
+
|
|
331
|
+
# Expo modules (if using Expo)
|
|
332
|
+
-keep class expo.modules.** { *; }
|
|
333
|
+
|
|
334
|
+
# OkHttp
|
|
335
|
+
-dontwarn okhttp3.**
|
|
336
|
+
-keep class okhttp3.** { *; }
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Enable in `android/app/build.gradle`:
|
|
340
|
+
|
|
341
|
+
```gradle
|
|
342
|
+
android {
|
|
343
|
+
buildTypes {
|
|
344
|
+
release {
|
|
345
|
+
minifyEnabled true
|
|
346
|
+
shrinkResources true
|
|
347
|
+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## iOS Privacy Manifest (Required for App Store)
|
|
354
|
+
|
|
355
|
+
Apple requires `PrivacyInfo.xcprivacy` since Spring 2024. Without it, your app will be rejected.
|
|
356
|
+
|
|
357
|
+
```xml
|
|
358
|
+
<!-- ios/YourApp/PrivacyInfo.xcprivacy -->
|
|
359
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
360
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
361
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
362
|
+
<plist version="1.0">
|
|
363
|
+
<dict>
|
|
364
|
+
<key>NSPrivacyTracking</key>
|
|
365
|
+
<false/>
|
|
366
|
+
<key>NSPrivacyTrackingDomains</key>
|
|
367
|
+
<array/>
|
|
368
|
+
<key>NSPrivacyCollectedDataTypes</key>
|
|
369
|
+
<array/>
|
|
370
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
371
|
+
<array>
|
|
372
|
+
<dict>
|
|
373
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
374
|
+
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
375
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
376
|
+
<array><string>CA92.1</string></array>
|
|
377
|
+
</dict>
|
|
378
|
+
<dict>
|
|
379
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
380
|
+
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
381
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
382
|
+
<array><string>C617.1</string></array>
|
|
383
|
+
</dict>
|
|
384
|
+
<dict>
|
|
385
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
386
|
+
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
|
387
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
388
|
+
<array><string>35F9.1</string></array>
|
|
389
|
+
</dict>
|
|
390
|
+
</array>
|
|
391
|
+
</dict>
|
|
392
|
+
</plist>
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Add to Xcode: Drag into app group → ensure "Copy Bundle Resources" includes it.
|
|
396
|
+
|
|
397
|
+
## Secure Signing Configuration
|
|
398
|
+
|
|
399
|
+
Never commit passwords to version control.
|
|
400
|
+
|
|
401
|
+
```gradle
|
|
402
|
+
// android/app/build.gradle — secure signing
|
|
403
|
+
signingConfigs {
|
|
404
|
+
release {
|
|
405
|
+
storeFile file(MYAPP_RELEASE_STORE_FILE ?: System.getenv("MYAPP_RELEASE_STORE_FILE") ?: "release.keystore")
|
|
406
|
+
storePassword MYAPP_RELEASE_STORE_PASSWORD ?: System.getenv("MYAPP_RELEASE_STORE_PASSWORD")
|
|
407
|
+
keyAlias MYAPP_RELEASE_KEY_ALIAS ?: System.getenv("MYAPP_RELEASE_KEY_ALIAS")
|
|
408
|
+
keyPassword MYAPP_RELEASE_KEY_PASSWORD ?: System.getenv("MYAPP_RELEASE_KEY_PASSWORD")
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```yaml
|
|
414
|
+
# GitHub Actions — decode keystore from secrets
|
|
415
|
+
- name: Decode Keystore
|
|
416
|
+
run: echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 --decode > android/app/release.keystore
|
|
417
|
+
- name: Build Release
|
|
418
|
+
env:
|
|
419
|
+
MYAPP_RELEASE_STORE_FILE: release.keystore
|
|
420
|
+
MYAPP_RELEASE_STORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
421
|
+
MYAPP_RELEASE_KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
422
|
+
MYAPP_RELEASE_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
423
|
+
run: cd android && ./gradlew bundleRelease
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Metro Production Configuration
|
|
427
|
+
|
|
428
|
+
```javascript
|
|
429
|
+
// metro.config.js
|
|
430
|
+
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
|
431
|
+
|
|
432
|
+
const config = {
|
|
433
|
+
transformer: {
|
|
434
|
+
minifierConfig: {
|
|
435
|
+
compress: {
|
|
436
|
+
drop_console: true, // Remove console.log in production
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
|
443
|
+
```
|