qualia-framework 2.5.1 → 3.1.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.
Files changed (327) hide show
  1. package/CLAUDE.md +63 -0
  2. package/README.md +108 -30
  3. package/agents/builder.md +110 -0
  4. package/agents/planner.md +186 -0
  5. package/agents/qa-browser.md +186 -0
  6. package/agents/verifier.md +369 -0
  7. package/bin/cli.js +706 -417
  8. package/bin/install.js +622 -0
  9. package/bin/qualia-ui.js +284 -0
  10. package/bin/state.js +824 -0
  11. package/bin/statusline.js +252 -0
  12. package/docs/erp-contract.md +161 -0
  13. package/guide.md +63 -0
  14. package/hooks/auto-update.js +117 -0
  15. package/hooks/block-env-edit.js +52 -0
  16. package/hooks/branch-guard.js +68 -0
  17. package/hooks/migration-guard.js +83 -0
  18. package/hooks/pre-compact.js +52 -0
  19. package/hooks/pre-deploy-gate.js +149 -0
  20. package/hooks/pre-push.js +53 -0
  21. package/hooks/session-start.js +126 -0
  22. package/package.json +31 -17
  23. package/rules/design-reference.md +179 -0
  24. package/rules/frontend.md +126 -0
  25. package/rules/infrastructure.md +87 -0
  26. package/skills/qualia/SKILL.md +88 -0
  27. package/skills/qualia-build/SKILL.md +115 -0
  28. package/skills/qualia-debug/SKILL.md +87 -0
  29. package/skills/qualia-design/SKILL.md +99 -0
  30. package/skills/qualia-handoff/SKILL.md +66 -0
  31. package/skills/qualia-help/SKILL.md +60 -0
  32. package/skills/qualia-idk/SKILL.md +8 -0
  33. package/skills/qualia-learn/SKILL.md +111 -0
  34. package/skills/qualia-new/SKILL.md +323 -0
  35. package/skills/qualia-pause/SKILL.md +63 -0
  36. package/skills/qualia-plan/SKILL.md +101 -0
  37. package/skills/qualia-polish/SKILL.md +207 -0
  38. package/skills/qualia-quick/SKILL.md +37 -0
  39. package/skills/qualia-report/SKILL.md +114 -0
  40. package/skills/qualia-resume/SKILL.md +49 -0
  41. package/skills/qualia-review/SKILL.md +161 -0
  42. package/skills/qualia-ship/SKILL.md +90 -0
  43. package/skills/qualia-skill-new/SKILL.md +167 -0
  44. package/skills/qualia-task/SKILL.md +91 -0
  45. package/skills/qualia-test/SKILL.md +134 -0
  46. package/skills/qualia-verify/SKILL.md +113 -0
  47. package/templates/DESIGN.md +475 -0
  48. package/templates/help.html +476 -0
  49. package/templates/plan.md +42 -0
  50. package/templates/project.md +22 -0
  51. package/templates/state.md +27 -0
  52. package/templates/tracking.json +20 -0
  53. package/tests/bin.test.sh +687 -0
  54. package/tests/hooks.test.sh +384 -0
  55. package/tests/runner.js +1956 -0
  56. package/tests/state.test.sh +713 -0
  57. package/tests/statusline.test.sh +243 -0
  58. package/bin/collect-metrics.sh +0 -62
  59. package/framework/.claudeignore +0 -51
  60. package/framework/CLAUDE.md +0 -51
  61. package/framework/MCP_SETUP.md +0 -229
  62. package/framework/agents/architecture-strategist.md +0 -53
  63. package/framework/agents/backend-agent.md +0 -150
  64. package/framework/agents/code-simplicity-reviewer.md +0 -86
  65. package/framework/agents/frontend-agent.md +0 -111
  66. package/framework/agents/kieran-typescript-reviewer.md +0 -96
  67. package/framework/agents/performance-oracle.md +0 -111
  68. package/framework/agents/qualia-codebase-mapper.md +0 -761
  69. package/framework/agents/qualia-debugger.md +0 -1204
  70. package/framework/agents/qualia-executor.md +0 -882
  71. package/framework/agents/qualia-integration-checker.md +0 -424
  72. package/framework/agents/qualia-phase-researcher.md +0 -457
  73. package/framework/agents/qualia-plan-checker.md +0 -700
  74. package/framework/agents/qualia-planner.md +0 -1245
  75. package/framework/agents/qualia-project-researcher.md +0 -603
  76. package/framework/agents/qualia-research-synthesizer.md +0 -200
  77. package/framework/agents/qualia-roadmapper.md +0 -606
  78. package/framework/agents/qualia-verifier.md +0 -686
  79. package/framework/agents/red-team-qa.md +0 -130
  80. package/framework/agents/security-auditor.md +0 -72
  81. package/framework/agents/team-orchestrator.md +0 -229
  82. package/framework/agents/teams/framework-audit-team.md +0 -66
  83. package/framework/agents/teams/full-stack-team.md +0 -48
  84. package/framework/agents/teams/optimize-team.md +0 -53
  85. package/framework/agents/teams/review-team.md +0 -70
  86. package/framework/agents/teams/ship-team.md +0 -86
  87. package/framework/agents/test-agent.md +0 -182
  88. package/framework/hooks/auto-format.sh +0 -54
  89. package/framework/hooks/block-env-edit.sh +0 -42
  90. package/framework/hooks/branch-guard.sh +0 -43
  91. package/framework/hooks/confirm-delete.sh +0 -59
  92. package/framework/hooks/migration-validate.sh +0 -77
  93. package/framework/hooks/notification-speak.sh +0 -16
  94. package/framework/hooks/pre-commit.sh +0 -100
  95. package/framework/hooks/pre-compact.sh +0 -56
  96. package/framework/hooks/pre-deploy-gate.sh +0 -160
  97. package/framework/hooks/qualia-colors.sh +0 -32
  98. package/framework/hooks/retention-cleanup.sh +0 -62
  99. package/framework/hooks/save-session-state.sh +0 -185
  100. package/framework/hooks/session-context-loader.sh +0 -96
  101. package/framework/hooks/session-learn.sh +0 -32
  102. package/framework/hooks/skill-announce.sh +0 -123
  103. package/framework/hooks/tool-error-announce.sh +0 -27
  104. package/framework/install.ps1 +0 -323
  105. package/framework/install.sh +0 -313
  106. package/framework/qualia-framework/VERSION +0 -1
  107. package/framework/qualia-framework/assets/qualia-logo.png +0 -0
  108. package/framework/qualia-framework/bin/collect-metrics.sh +0 -67
  109. package/framework/qualia-framework/bin/generate-report-docx.py +0 -429
  110. package/framework/qualia-framework/bin/qualia-tools.js +0 -2201
  111. package/framework/qualia-framework/bin/qualia-tools.test.js +0 -1054
  112. package/framework/qualia-framework/references/checkpoints.md +0 -775
  113. package/framework/qualia-framework/references/completion-checklists.md +0 -359
  114. package/framework/qualia-framework/references/continuation-format.md +0 -249
  115. package/framework/qualia-framework/references/continuation-prompt.md +0 -97
  116. package/framework/qualia-framework/references/decimal-phase-calculation.md +0 -65
  117. package/framework/qualia-framework/references/design-quality.md +0 -56
  118. package/framework/qualia-framework/references/employee-guide.md +0 -167
  119. package/framework/qualia-framework/references/git-integration.md +0 -254
  120. package/framework/qualia-framework/references/git-planning-commit.md +0 -50
  121. package/framework/qualia-framework/references/model-profile-resolution.md +0 -32
  122. package/framework/qualia-framework/references/model-profiles.md +0 -73
  123. package/framework/qualia-framework/references/phase-argument-parsing.md +0 -61
  124. package/framework/qualia-framework/references/planning-config.md +0 -195
  125. package/framework/qualia-framework/references/questioning.md +0 -141
  126. package/framework/qualia-framework/references/tdd.md +0 -263
  127. package/framework/qualia-framework/references/ui-brand.md +0 -160
  128. package/framework/qualia-framework/references/verification-patterns.md +0 -612
  129. package/framework/qualia-framework/templates/DEBUG.md +0 -159
  130. package/framework/qualia-framework/templates/DESIGN.md +0 -81
  131. package/framework/qualia-framework/templates/UAT.md +0 -247
  132. package/framework/qualia-framework/templates/codebase/architecture.md +0 -255
  133. package/framework/qualia-framework/templates/codebase/concerns.md +0 -310
  134. package/framework/qualia-framework/templates/codebase/conventions.md +0 -307
  135. package/framework/qualia-framework/templates/codebase/integrations.md +0 -280
  136. package/framework/qualia-framework/templates/codebase/stack.md +0 -186
  137. package/framework/qualia-framework/templates/codebase/structure.md +0 -285
  138. package/framework/qualia-framework/templates/codebase/testing.md +0 -480
  139. package/framework/qualia-framework/templates/config.json +0 -35
  140. package/framework/qualia-framework/templates/context.md +0 -283
  141. package/framework/qualia-framework/templates/continue-here.md +0 -78
  142. package/framework/qualia-framework/templates/debug-subagent-prompt.md +0 -91
  143. package/framework/qualia-framework/templates/discovery.md +0 -146
  144. package/framework/qualia-framework/templates/lab-notes.md +0 -16
  145. package/framework/qualia-framework/templates/milestone-archive.md +0 -123
  146. package/framework/qualia-framework/templates/milestone.md +0 -115
  147. package/framework/qualia-framework/templates/phase-prompt.md +0 -567
  148. package/framework/qualia-framework/templates/planner-subagent-prompt.md +0 -117
  149. package/framework/qualia-framework/templates/project.md +0 -184
  150. package/framework/qualia-framework/templates/projects/ai-agent.md +0 -156
  151. package/framework/qualia-framework/templates/projects/mobile-app.md +0 -181
  152. package/framework/qualia-framework/templates/projects/voice-agent.md +0 -134
  153. package/framework/qualia-framework/templates/projects/website.md +0 -137
  154. package/framework/qualia-framework/templates/requirements.md +0 -231
  155. package/framework/qualia-framework/templates/research-project/ARCHITECTURE.md +0 -204
  156. package/framework/qualia-framework/templates/research-project/FEATURES.md +0 -147
  157. package/framework/qualia-framework/templates/research-project/PITFALLS.md +0 -200
  158. package/framework/qualia-framework/templates/research-project/STACK.md +0 -120
  159. package/framework/qualia-framework/templates/research-project/SUMMARY.md +0 -170
  160. package/framework/qualia-framework/templates/research.md +0 -552
  161. package/framework/qualia-framework/templates/roadmap.md +0 -206
  162. package/framework/qualia-framework/templates/state.md +0 -179
  163. package/framework/qualia-framework/templates/summary-complex.md +0 -59
  164. package/framework/qualia-framework/templates/summary-minimal.md +0 -41
  165. package/framework/qualia-framework/templates/summary-standard.md +0 -48
  166. package/framework/qualia-framework/templates/summary.md +0 -246
  167. package/framework/qualia-framework/templates/user-setup.md +0 -311
  168. package/framework/qualia-framework/templates/verification-report.md +0 -322
  169. package/framework/qualia-framework/workflows/add-phase.md +0 -179
  170. package/framework/qualia-framework/workflows/add-todo.md +0 -157
  171. package/framework/qualia-framework/workflows/audit-milestone.md +0 -241
  172. package/framework/qualia-framework/workflows/check-todos.md +0 -176
  173. package/framework/qualia-framework/workflows/complete-milestone.md +0 -858
  174. package/framework/qualia-framework/workflows/diagnose-issues.md +0 -219
  175. package/framework/qualia-framework/workflows/discovery-phase.md +0 -289
  176. package/framework/qualia-framework/workflows/discuss-phase.md +0 -534
  177. package/framework/qualia-framework/workflows/execute-phase.md +0 -559
  178. package/framework/qualia-framework/workflows/execute-plan.md +0 -438
  179. package/framework/qualia-framework/workflows/help.md +0 -470
  180. package/framework/qualia-framework/workflows/insert-phase.md +0 -220
  181. package/framework/qualia-framework/workflows/list-phase-assumptions.md +0 -178
  182. package/framework/qualia-framework/workflows/map-codebase.md +0 -327
  183. package/framework/qualia-framework/workflows/new-milestone.md +0 -363
  184. package/framework/qualia-framework/workflows/new-project.md +0 -982
  185. package/framework/qualia-framework/workflows/pause-work.md +0 -122
  186. package/framework/qualia-framework/workflows/plan-milestone-gaps.md +0 -256
  187. package/framework/qualia-framework/workflows/plan-phase.md +0 -422
  188. package/framework/qualia-framework/workflows/progress.md +0 -389
  189. package/framework/qualia-framework/workflows/quick.md +0 -252
  190. package/framework/qualia-framework/workflows/remove-phase.md +0 -326
  191. package/framework/qualia-framework/workflows/research-phase.md +0 -74
  192. package/framework/qualia-framework/workflows/resume-project.md +0 -306
  193. package/framework/qualia-framework/workflows/set-profile.md +0 -80
  194. package/framework/qualia-framework/workflows/settings.md +0 -145
  195. package/framework/qualia-framework/workflows/transition.md +0 -556
  196. package/framework/qualia-framework/workflows/update.md +0 -197
  197. package/framework/qualia-framework/workflows/verify-phase.md +0 -195
  198. package/framework/qualia-framework/workflows/verify-work.md +0 -625
  199. package/framework/rules/context7.md +0 -14
  200. package/framework/rules/frontend.md +0 -33
  201. package/framework/rules/speed.md +0 -23
  202. package/framework/scripts/__pycache__/say.cpython-314.pyc +0 -0
  203. package/framework/scripts/apply-retention.sh +0 -120
  204. package/framework/scripts/bootstrap-pop-os.sh +0 -354
  205. package/framework/scripts/claude-voice +0 -13
  206. package/framework/scripts/cleanup.sh +0 -131
  207. package/framework/scripts/cowork-mode.sh +0 -141
  208. package/framework/scripts/generate-project-claude-md.sh +0 -153
  209. package/framework/scripts/load-test-webhook.js +0 -172
  210. package/framework/scripts/say.py +0 -236
  211. package/framework/scripts/showcase-video-recorder/ffmpeg-builder.js +0 -167
  212. package/framework/scripts/showcase-video-recorder/playwright-helpers.js +0 -216
  213. package/framework/scripts/speak.py +0 -55
  214. package/framework/scripts/speak.sh +0 -18
  215. package/framework/scripts/status.sh +0 -138
  216. package/framework/scripts/sync-to-framework.sh +0 -65
  217. package/framework/scripts/voice-hotkey.py +0 -227
  218. package/framework/scripts/voice-input.sh +0 -51
  219. package/framework/skills/animate/SKILL.md +0 -202
  220. package/framework/skills/bolder/SKILL.md +0 -144
  221. package/framework/skills/browser-qa/SKILL.md +0 -536
  222. package/framework/skills/clarify/SKILL.md +0 -179
  223. package/framework/skills/client-handoff/SKILL.md +0 -135
  224. package/framework/skills/collab-onboard/SKILL.md +0 -111
  225. package/framework/skills/colorize/SKILL.md +0 -170
  226. package/framework/skills/critique/SKILL.md +0 -126
  227. package/framework/skills/deep-research/SKILL.md +0 -240
  228. package/framework/skills/delight/SKILL.md +0 -329
  229. package/framework/skills/deploy/SKILL.md +0 -261
  230. package/framework/skills/deploy-verify/SKILL.md +0 -377
  231. package/framework/skills/deploy-verify/scripts/canary-check.sh +0 -206
  232. package/framework/skills/deploy-verify/scripts/check-console-errors.js +0 -147
  233. package/framework/skills/deploy-verify/scripts/check-cwv.js +0 -139
  234. package/framework/skills/deploy-verify/scripts/project-detect.sh +0 -84
  235. package/framework/skills/deploy-verify/scripts/verify.sh +0 -548
  236. package/framework/skills/design-quieter/SKILL.md +0 -130
  237. package/framework/skills/distill/SKILL.md +0 -149
  238. package/framework/skills/docs-lookup/SKILL.md +0 -79
  239. package/framework/skills/fcm-notifications/SKILL.md +0 -125
  240. package/framework/skills/financial-ledger/SKILL.md +0 -1039
  241. package/framework/skills/frontend-master/NOTICE.md +0 -4
  242. package/framework/skills/frontend-master/SKILL.md +0 -127
  243. package/framework/skills/frontend-master/reference/color-and-contrast.md +0 -132
  244. package/framework/skills/frontend-master/reference/interaction-design.md +0 -123
  245. package/framework/skills/frontend-master/reference/motion-design.md +0 -99
  246. package/framework/skills/frontend-master/reference/responsive-design.md +0 -114
  247. package/framework/skills/frontend-master/reference/spatial-design.md +0 -100
  248. package/framework/skills/frontend-master/reference/typography.md +0 -131
  249. package/framework/skills/frontend-master/reference/ux-writing.md +0 -107
  250. package/framework/skills/harden/SKILL.md +0 -357
  251. package/framework/skills/i18n-rtl/SKILL.md +0 -752
  252. package/framework/skills/learn/SKILL.md +0 -95
  253. package/framework/skills/memory/SKILL.md +0 -50
  254. package/framework/skills/mobile-expo/SKILL.md +0 -977
  255. package/framework/skills/mobile-expo/references/store-checklist.md +0 -550
  256. package/framework/skills/nestjs-backend/README.md +0 -73
  257. package/framework/skills/nestjs-backend/SKILL.md +0 -446
  258. package/framework/skills/nestjs-backend/references/templates.md +0 -1173
  259. package/framework/skills/normalize/SKILL.md +0 -79
  260. package/framework/skills/onboard/SKILL.md +0 -242
  261. package/framework/skills/openrouter-agent/SKILL.md +0 -922
  262. package/framework/skills/polish/SKILL.md +0 -209
  263. package/framework/skills/pr/SKILL.md +0 -66
  264. package/framework/skills/qualia/SKILL.md +0 -199
  265. package/framework/skills/qualia-add-todo/SKILL.md +0 -68
  266. package/framework/skills/qualia-audit-milestone/SKILL.md +0 -95
  267. package/framework/skills/qualia-check-todos/SKILL.md +0 -55
  268. package/framework/skills/qualia-complete-milestone/SKILL.md +0 -134
  269. package/framework/skills/qualia-debug/SKILL.md +0 -149
  270. package/framework/skills/qualia-design/SKILL.md +0 -203
  271. package/framework/skills/qualia-discuss-phase/SKILL.md +0 -72
  272. package/framework/skills/qualia-evolve/SKILL.md +0 -200
  273. package/framework/skills/qualia-execute-phase/SKILL.md +0 -89
  274. package/framework/skills/qualia-framework-audit/SKILL.md +0 -604
  275. package/framework/skills/qualia-guide/SKILL.md +0 -32
  276. package/framework/skills/qualia-help/SKILL.md +0 -114
  277. package/framework/skills/qualia-idk/SKILL.md +0 -352
  278. package/framework/skills/qualia-list-phase-assumptions/SKILL.md +0 -67
  279. package/framework/skills/qualia-new-milestone/SKILL.md +0 -72
  280. package/framework/skills/qualia-new-project/SKILL.md +0 -232
  281. package/framework/skills/qualia-optimize/SKILL.md +0 -417
  282. package/framework/skills/qualia-pause-work/SKILL.md +0 -96
  283. package/framework/skills/qualia-plan-milestone-gaps/SKILL.md +0 -57
  284. package/framework/skills/qualia-plan-phase/SKILL.md +0 -104
  285. package/framework/skills/qualia-production-check/SKILL.md +0 -0
  286. package/framework/skills/qualia-progress/SKILL.md +0 -53
  287. package/framework/skills/qualia-quick/SKILL.md +0 -89
  288. package/framework/skills/qualia-report/SKILL.md +0 -166
  289. package/framework/skills/qualia-research-phase/SKILL.md +0 -88
  290. package/framework/skills/qualia-resume-work/SKILL.md +0 -62
  291. package/framework/skills/qualia-review/SKILL.md +0 -263
  292. package/framework/skills/qualia-start/SKILL.md +0 -161
  293. package/framework/skills/qualia-verify-work/SKILL.md +0 -132
  294. package/framework/skills/rag/SKILL.md +0 -750
  295. package/framework/skills/responsive/SKILL.md +0 -231
  296. package/framework/skills/retro/SKILL.md +0 -284
  297. package/framework/skills/sakani-conventions/SKILL.md +0 -136
  298. package/framework/skills/sakani-conventions/evals/evals.json +0 -23
  299. package/framework/skills/sakani-conventions/references/entities.md +0 -365
  300. package/framework/skills/sakani-conventions/references/error-codes.md +0 -95
  301. package/framework/skills/seo-master/SKILL.md +0 -490
  302. package/framework/skills/seo-master/references/checklist.md +0 -199
  303. package/framework/skills/seo-master/references/structured-data.md +0 -609
  304. package/framework/skills/ship/SKILL.md +0 -239
  305. package/framework/skills/stack-researcher/SKILL.md +0 -215
  306. package/framework/skills/status/SKILL.md +0 -154
  307. package/framework/skills/status/scripts/health-check.sh +0 -562
  308. package/framework/skills/subscription-payments/SKILL.md +0 -250
  309. package/framework/skills/supabase/SKILL.md +0 -973
  310. package/framework/skills/supabase/references/templates.md +0 -159
  311. package/framework/skills/team/SKILL.md +0 -67
  312. package/framework/skills/test-runner/SKILL.md +0 -202
  313. package/framework/skills/voice-agent/SKILL.md +0 -1312
  314. package/framework/skills/zoho-workflow/SKILL.md +0 -51
  315. package/framework/statusline-command.sh +0 -117
  316. package/framework/teams/default/inboxes/plan-04.json +0 -9
  317. package/framework/teams/review-team.md +0 -75
  318. package/framework/teams/ship-team.md +0 -86
  319. package/profiles/fawzi.json +0 -16
  320. package/profiles/hasan.json +0 -16
  321. package/profiles/moayad.json +0 -16
  322. package/templates/CLAUDE-owner.md +0 -52
  323. package/templates/CLAUDE.md.hbs +0 -58
  324. package/templates/env.claude.template +0 -12
  325. package/templates/settings.json +0 -172
  326. /package/{framework/rules → rules}/deployment.md +0 -0
  327. /package/{framework/rules → rules}/security.md +0 -0
@@ -1,977 +0,0 @@
1
- ---
2
- name: mobile-expo
3
- description: "React Native Expo mobile app development — project setup, navigation (Expo Router), native modules, push notifications, secure storage, app store submission, and mobile-specific patterns. Use whenever building or modifying a React Native or Expo mobile app, creating screens, navigation flows, native integrations, or preparing for iOS/Android store submission. Triggers on: React Native, Expo, mobile app, iOS, Android, app store, screen, navigation, native module, mobile UI, push notification mobile, secure storage, EAS build."
4
- tags: [react-native, expo, mobile, ios, android]
5
- ---
6
-
7
- # React Native Expo Mobile Development
8
-
9
- This skill covers React Native Expo mobile app development for Qualia workflow projects.
10
-
11
- ## Project Setup
12
-
13
- ### Expo SDK 52+
14
- - Always use latest stable Expo SDK (52+)
15
- - TypeScript is mandatory for all projects
16
- - Use Expo Router for file-based routing (like Next.js)
17
- - Configured via `app.json` or `app.config.ts`
18
-
19
- ### Directory Structure
20
-
21
- ```
22
- app/
23
- ├── (auth)/ # Protected routes (require authentication)
24
- │ ├── (tabs)/ # Bottom tab navigator (protected)
25
- │ │ ├── home.tsx
26
- │ │ ├── units.tsx
27
- │ │ └── profile.tsx
28
- │ │ └── _layout.tsx # Tab layout configuration
29
- │ ├── _layout.tsx # Auth wrapper layout
30
- │ └── [route].tsx # Catch-all protected route
31
- ├── (public)/ # Unauthenticated screens
32
- │ ├── login.tsx
33
- │ ├── register.tsx
34
- │ ├── verify-otp.tsx
35
- │ └── _layout.tsx
36
- ├── _layout.tsx # Root layout
37
- ├── index.tsx # Root screen (can redirect)
38
- └── +not-found.tsx # 404 screen
39
-
40
- components/
41
- ├── ui/ # Reusable, unstyled UI primitives
42
- │ ├── Button.tsx
43
- │ ├── Input.tsx
44
- │ ├── Modal.tsx
45
- │ └── Card.tsx
46
- ├── forms/ # Form-specific components
47
- │ ├── LoginForm.tsx
48
- │ ├── OTPForm.tsx
49
- │ └── ProfileForm.tsx
50
- ├── auth/ # Auth-related components
51
- │ ├── ProtectedRoute.tsx
52
- │ └── AuthGuard.tsx
53
- └── [feature]/ # Feature-specific components
54
- ├── UnitsCard.tsx
55
- └── UnitsList.tsx
56
-
57
- lib/
58
- ├── api/ # API client layer
59
- │ ├── client.ts # Axios/fetch with interceptors
60
- │ ├── endpoints.ts # API endpoint definitions
61
- │ └── schemas.ts # Zod schemas (shared with backend)
62
- ├── auth/ # Authentication logic
63
- │ ├── context.tsx # Auth context provider
64
- │ ├── tokens.ts # Token management
65
- │ └── useAuth.ts # Auth hook
66
- ├── storage/ # Secure storage wrapper
67
- │ ├── secureStore.ts # expo-secure-store wrapper
68
- │ └── useSecureStorage.ts # Custom hook
69
- ├── i18n/ # Internationalization (if needed)
70
- │ └── i18n.ts
71
- ├── utils/ # Helper functions
72
- └── constants.ts # App-wide constants
73
-
74
- hooks/
75
- ├── useAuthCheck.ts # Check if user is authenticated
76
- ├── useNavigation.ts # Type-safe navigation hook
77
- ├── useFetch.ts # Custom hook for API calls
78
- └── useKeyboard.ts # Keyboard handling
79
-
80
- constants/
81
- ├── theme.ts # Colors, typography, spacing
82
- └── config.ts # API base URL, feature flags
83
-
84
- assets/
85
- ├── icons/
86
- ├── images/
87
- └── animations/
88
-
89
- App.tsx or app.json # Entry point configuration
90
- ```
91
-
92
- ## Navigation with Expo Router
93
-
94
- ### File-Based Routing
95
- - Expo Router uses file system paths as routes
96
- - `app/home.tsx` → `/home` route
97
- - `app/(tabs)/home.tsx` → `/home` with tab layout
98
- - `app/(auth)/profile.tsx` → Protected `/profile` route
99
- - Deep linking is automatic with proper configuration
100
-
101
- ### Layout Groups
102
- Layout groups with parentheses don't create route segments:
103
- - `(auth)` group shares auth layout without adding to URL
104
- - `(public)` group for unauthenticated screens
105
- - `(tabs)` group for tab navigation within auth group
106
-
107
- ### Protected Routes Pattern
108
-
109
- ```tsx
110
- // app/(auth)/_layout.tsx
111
- import { useAuth } from '@/lib/auth/useAuth';
112
- import { Redirect, Stack } from 'expo-router';
113
-
114
- export default function AuthLayout() {
115
- const { isAuthenticated, isLoading } = useAuth();
116
-
117
- if (isLoading) return <SplashScreen />;
118
- if (!isAuthenticated) return <Redirect href="/login" />;
119
-
120
- return <Stack />;
121
- }
122
-
123
- // app/(auth)/(tabs)/_layout.tsx
124
- import { Tabs } from 'expo-router';
125
-
126
- export default function TabsLayout() {
127
- return (
128
- <Tabs>
129
- <Tabs.Screen name="home" options={{ title: 'Home' }} />
130
- <Tabs.Screen name="units" options={{ title: 'Units' }} />
131
- <Tabs.Screen name="profile" options={{ title: 'Profile' }} />
132
- </Tabs>
133
- );
134
- }
135
- ```
136
-
137
- ### Type-Safe Navigation
138
- Create a custom hook for navigation with proper typing:
139
-
140
- ```tsx
141
- // lib/navigation/useTypedNavigation.ts
142
- import { useNavigation } from '@react-navigation/native';
143
- import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
144
-
145
- type RootStackParamList = {
146
- '/(public)/login': undefined;
147
- '/(auth)/(tabs)/home': undefined;
148
- '/(auth)/(tabs)/units': { unitId: string };
149
- '/(auth)/profile': undefined;
150
- };
151
-
152
- export function useTypedNavigation() {
153
- return useNavigation<NativeStackNavigationProp<RootStackParamList>>();
154
- }
155
- ```
156
-
157
- ### Deep Linking
158
- Configure in `app.json`:
159
-
160
- ```json
161
- {
162
- "scheme": "myapp",
163
- "plugins": [
164
- ["expo-router", {
165
- "origin": "https://myapp.com"
166
- }]
167
- ]
168
- }
169
- ```
170
-
171
- ## API Client Setup
172
-
173
- ### Centralized API Client
174
-
175
- ```tsx
176
- // lib/api/client.ts
177
- import axios, { AxiosInstance, AxiosError } from 'axios';
178
- import { getAccessToken, getRefreshToken, setAccessToken } from '@/lib/auth/tokens';
179
-
180
- const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || 'http://localhost:3000';
181
-
182
- let client: AxiosInstance;
183
-
184
- export function createApiClient(): AxiosInstance {
185
- client = axios.create({
186
- baseURL: API_BASE_URL,
187
- headers: {
188
- 'Content-Type': 'application/json',
189
- },
190
- });
191
-
192
- // Request interceptor: attach access token
193
- client.interceptors.request.use(async (config) => {
194
- const token = await getAccessToken();
195
- if (token) {
196
- config.headers.Authorization = `Bearer ${token}`;
197
- }
198
- return config;
199
- });
200
-
201
- // Response interceptor: handle 401 with refresh
202
- client.interceptors.response.use(
203
- (response) => response,
204
- async (error: AxiosError) => {
205
- const originalRequest = error.config as any;
206
-
207
- if (error.response?.status === 401 && !originalRequest._retry) {
208
- originalRequest._retry = true;
209
-
210
- try {
211
- const refreshToken = await getRefreshToken();
212
- const { data } = await axios.post(`${API_BASE_URL}/auth/refresh`, {
213
- refreshToken,
214
- });
215
-
216
- await setAccessToken(data.accessToken);
217
- originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
218
- return client(originalRequest);
219
- } catch (refreshError) {
220
- // Refresh failed, redirect to login
221
- // Trigger logout in auth context
222
- throw refreshError;
223
- }
224
- }
225
-
226
- return Promise.reject(error);
227
- }
228
- );
229
-
230
- return client;
231
- }
232
-
233
- export function getApiClient(): AxiosInstance {
234
- return client || createApiClient();
235
- }
236
- ```
237
-
238
- ### Endpoint Definitions
239
-
240
- ```tsx
241
- // lib/api/endpoints.ts
242
- export const API_ENDPOINTS = {
243
- AUTH: {
244
- LOGIN: '/auth/login',
245
- VERIFY_OTP: '/auth/verify-otp',
246
- REFRESH: '/auth/refresh',
247
- LOGOUT: '/auth/logout',
248
- },
249
- UNITS: {
250
- LIST: '/units',
251
- GET: (id: string) => `/units/${id}`,
252
- CREATE: '/units',
253
- UPDATE: (id: string) => `/units/${id}`,
254
- },
255
- PROFILE: {
256
- GET: '/profile',
257
- UPDATE: '/profile',
258
- },
259
- } as const;
260
- ```
261
-
262
- ## Authentication Flow (Mobile)
263
-
264
- ### Token Management
265
-
266
- Access tokens are short-lived (15-30 min) and stored in-memory.
267
- Refresh tokens are long-lived and stored in secure storage.
268
-
269
- ```tsx
270
- // lib/auth/tokens.ts
271
- import * as SecureStore from 'expo-secure-store';
272
-
273
- const ACCESS_TOKEN_KEY = 'accessToken';
274
- const REFRESH_TOKEN_KEY = 'refreshToken';
275
-
276
- let accessToken: string | null = null;
277
-
278
- export async function setTokens(access: string, refresh: string) {
279
- accessToken = access;
280
- await SecureStore.setItemAsync(REFRESH_TOKEN_KEY, refresh);
281
- }
282
-
283
- export async function getAccessToken(): Promise<string | null> {
284
- return accessToken;
285
- }
286
-
287
- export async function getRefreshToken(): Promise<string | null> {
288
- return await SecureStore.getItemAsync(REFRESH_TOKEN_KEY);
289
- }
290
-
291
- export async function clearTokens() {
292
- accessToken = null;
293
- await SecureStore.deleteItemAsync(REFRESH_TOKEN_KEY);
294
- }
295
-
296
- export async function initializeTokens() {
297
- // Called on app start
298
- // Access token stays in memory, refresh token loaded from secure store
299
- // Don't restore access token to memory (it expired)
300
- }
301
- ```
302
-
303
- ### Auth Context
304
-
305
- ```tsx
306
- // lib/auth/context.tsx
307
- import React, { createContext, useState, useEffect } from 'react';
308
- import { AppState } from 'react-native';
309
- import { getApiClient } from '@/lib/api/client';
310
- import { setTokens, clearTokens, getRefreshToken } from './tokens';
311
-
312
- interface AuthContextType {
313
- isAuthenticated: boolean;
314
- isLoading: boolean;
315
- user: { id: string; email: string } | null;
316
- login: (email: string, otp: string) => Promise<void>;
317
- logout: () => Promise<void>;
318
- refreshAuth: () => Promise<void>;
319
- }
320
-
321
- export const AuthContext = createContext<AuthContextType | undefined>(undefined);
322
-
323
- export function AuthProvider({ children }: { children: React.ReactNode }) {
324
- const [isAuthenticated, setIsAuthenticated] = useState(false);
325
- const [isLoading, setIsLoading] = useState(true);
326
- const [user, setUser] = useState<AuthContextType['user']>(null);
327
-
328
- // Initialize auth on app start
329
- useEffect(() => {
330
- const initAuth = async () => {
331
- try {
332
- const refreshToken = await getRefreshToken();
333
- if (refreshToken) {
334
- await refreshAuth();
335
- } else {
336
- setIsAuthenticated(false);
337
- }
338
- } finally {
339
- setIsLoading(false);
340
- }
341
- };
342
-
343
- initAuth();
344
- }, []);
345
-
346
- // Listen for app foreground (auto-refresh tokens)
347
- useEffect(() => {
348
- const subscription = AppState.addEventListener('focus', refreshAuth);
349
- return () => subscription.remove();
350
- }, []);
351
-
352
- const login = async (email: string, otp: string) => {
353
- const api = getApiClient();
354
- const { data } = await api.post('/auth/verify-otp', { email, otp });
355
- await setTokens(data.accessToken, data.refreshToken);
356
- setUser(data.user);
357
- setIsAuthenticated(true);
358
- };
359
-
360
- const logout = async () => {
361
- await clearTokens();
362
- setUser(null);
363
- setIsAuthenticated(false);
364
- };
365
-
366
- const refreshAuth = async () => {
367
- try {
368
- const api = getApiClient();
369
- const { data } = await api.post('/auth/refresh');
370
- await setTokens(data.accessToken, data.refreshToken);
371
- setUser(data.user);
372
- setIsAuthenticated(true);
373
- } catch {
374
- await logout();
375
- }
376
- };
377
-
378
- return (
379
- <AuthContext.Provider value={{ isAuthenticated, isLoading, user, login, logout, refreshAuth }}>
380
- {children}
381
- </AuthContext.Provider>
382
- );
383
- }
384
-
385
- export function useAuth() {
386
- const context = React.useContext(AuthContext);
387
- if (!context) {
388
- throw new Error('useAuth must be used within AuthProvider');
389
- }
390
- return context;
391
- }
392
- ```
393
-
394
- ### OTP Login Screen
395
-
396
- ```tsx
397
- // app/(public)/verify-otp.tsx
398
- import { useState } from 'react';
399
- import { View, Text, TextInput, TouchableOpacity, Alert } from 'react-native';
400
- import { useAuth } from '@/lib/auth/context';
401
- import { useRouter } from 'expo-router';
402
-
403
- export default function VerifyOTPScreen() {
404
- const [otp, setOtp] = useState('');
405
- const [loading, setLoading] = useState(false);
406
- const { login } = useAuth();
407
- const router = useRouter();
408
-
409
- const handleVerify = async () => {
410
- try {
411
- setLoading(true);
412
- await login('user@example.com', otp); // Email should come from previous screen
413
- router.replace('/(auth)/(tabs)/home');
414
- } catch (error) {
415
- Alert.alert('Error', 'Invalid OTP');
416
- } finally {
417
- setLoading(false);
418
- }
419
- };
420
-
421
- return (
422
- <View style={{ flex: 1, padding: 16, justifyContent: 'center' }}>
423
- <Text style={{ fontSize: 20, marginBottom: 16 }}>Enter OTP</Text>
424
- <TextInput
425
- placeholder="000000"
426
- value={otp}
427
- onChangeText={setOtp}
428
- keyboardType="number-pad"
429
- maxLength={6}
430
- style={{ borderWidth: 1, padding: 12, marginBottom: 16 }}
431
- />
432
- <TouchableOpacity
433
- onPress={handleVerify}
434
- disabled={loading}
435
- style={{ backgroundColor: '#007AFF', padding: 12, borderRadius: 8 }}
436
- >
437
- <Text style={{ color: 'white', textAlign: 'center' }}>Verify</Text>
438
- </TouchableOpacity>
439
- </View>
440
- );
441
- }
442
- ```
443
-
444
- ## Secure Storage
445
-
446
- ### SecureStore for Sensitive Data
447
-
448
- Never use AsyncStorage for tokens or secrets. Use `expo-secure-store`:
449
-
450
- ```tsx
451
- // lib/storage/secureStore.ts
452
- import * as SecureStore from 'expo-secure-store';
453
-
454
- const CHUNK_SIZE = 1800; // SecureStore limit is ~2KB per key
455
-
456
- export async function secureSet(key: string, value: string): Promise<void> {
457
- if (value.length > CHUNK_SIZE) {
458
- // Chunk large values
459
- for (let i = 0; i < value.length; i += CHUNK_SIZE) {
460
- const chunk = value.substring(i, i + CHUNK_SIZE);
461
- const chunkKey = `${key}_${Math.floor(i / CHUNK_SIZE)}`;
462
- await SecureStore.setItemAsync(chunkKey, chunk);
463
- }
464
- await SecureStore.setItemAsync(`${key}_chunks`, Math.ceil(value.length / CHUNK_SIZE).toString());
465
- } else {
466
- await SecureStore.setItemAsync(key, value);
467
- }
468
- }
469
-
470
- export async function secureGet(key: string): Promise<string | null> {
471
- const chunks = await SecureStore.getItemAsync(`${key}_chunks`);
472
- if (chunks) {
473
- let value = '';
474
- for (let i = 0; i < parseInt(chunks); i++) {
475
- const chunk = await SecureStore.getItemAsync(`${key}_${i}`);
476
- if (chunk) value += chunk;
477
- }
478
- return value || null;
479
- }
480
- return await SecureStore.getItemAsync(key);
481
- }
482
-
483
- export async function secureRemove(key: string): Promise<void> {
484
- const chunks = await SecureStore.getItemAsync(`${key}_chunks`);
485
- if (chunks) {
486
- for (let i = 0; i < parseInt(chunks); i++) {
487
- await SecureStore.deleteItemAsync(`${key}_${i}`);
488
- }
489
- await SecureStore.deleteItemAsync(`${key}_chunks`);
490
- } else {
491
- await SecureStore.deleteItemAsync(key);
492
- }
493
- }
494
- ```
495
-
496
- ## State Management
497
-
498
- ### React Context (Auth)
499
- Use Context for small, global state like authentication.
500
-
501
- ### TanStack Query (Server State)
502
- Use React Query for API data caching, refetching, and synchronization:
503
-
504
- ```tsx
505
- // lib/api/queries.ts
506
- import { useQuery, useMutation } from '@tanstack/react-query';
507
- import { getApiClient } from './client';
508
-
509
- export function useUnits() {
510
- return useQuery({
511
- queryKey: ['units'],
512
- queryFn: async () => {
513
- const { data } = await getApiClient().get('/units');
514
- return data;
515
- },
516
- });
517
- }
518
-
519
- export function useCreateUnit() {
520
- return useMutation({
521
- mutationFn: async (unit: any) => {
522
- const { data } = await getApiClient().post('/units', unit);
523
- return data;
524
- },
525
- onSuccess: (data, variables, context) => {
526
- // Invalidate and refetch units list
527
- queryClient.invalidateQueries({ queryKey: ['units'] });
528
- },
529
- });
530
- }
531
- ```
532
-
533
- ### Zustand (Complex Client State)
534
- Use Zustand for complex client state if needed:
535
-
536
- ```tsx
537
- // lib/store/appStore.ts
538
- import { create } from 'zustand';
539
-
540
- interface AppState {
541
- theme: 'light' | 'dark';
542
- setTheme: (theme: 'light' | 'dark') => void;
543
- }
544
-
545
- export const useAppStore = create<AppState>((set) => ({
546
- theme: 'light',
547
- setTheme: (theme) => set({ theme }),
548
- }));
549
- ```
550
-
551
- ## Push Notifications
552
-
553
- ### Setup with expo-notifications
554
-
555
- ```bash
556
- npx expo install expo-notifications expo-device
557
- ```
558
-
559
- ### Request Permissions
560
-
561
- ```tsx
562
- // lib/notifications/index.ts
563
- import * as Notifications from 'expo-notifications';
564
- import * as Device from 'expo-device';
565
-
566
- export async function requestNotificationPermissions(): Promise<boolean> {
567
- if (!Device.isDevice) {
568
- console.log('Must use physical device for push notifications');
569
- return false;
570
- }
571
-
572
- const { status: existingStatus } = await Notifications.getPermissionsAsync();
573
- let finalStatus = existingStatus;
574
-
575
- if (existingStatus !== 'granted') {
576
- const { status } = await Notifications.requestPermissionsAsync();
577
- finalStatus = status;
578
- }
579
-
580
- return finalStatus === 'granted';
581
- }
582
-
583
- export async function getDeviceToken(): Promise<string | null> {
584
- const token = (await Notifications.getExpoPushTokenAsync()).data;
585
- return token;
586
- }
587
-
588
- // Register device token with backend
589
- export async function registerDeviceToken(token: string) {
590
- const api = getApiClient();
591
- await api.post('/notifications/register', { deviceToken: token });
592
- }
593
- ```
594
-
595
- ### Listen for Notifications
596
-
597
- ```tsx
598
- // Set default notification handler
599
- Notifications.setNotificationHandler({
600
- handleNotification: async () => ({
601
- shouldShowAlert: true,
602
- shouldPlaySound: true,
603
- shouldSetBadge: true,
604
- }),
605
- });
606
-
607
- // Listen in your app
608
- useEffect(() => {
609
- const subscription = Notifications.addNotificationResponseReceivedListener((response) => {
610
- // Handle notification tap
611
- const route = response.notification.request.content.data?.route;
612
- if (route) {
613
- router.push(route);
614
- }
615
- });
616
-
617
- return () => subscription.remove();
618
- }, []);
619
- ```
620
-
621
- ## Forms with react-hook-form + Zod
622
-
623
- ```tsx
624
- // lib/api/schemas.ts (shared with backend)
625
- import { z } from 'zod';
626
-
627
- export const LoginSchema = z.object({
628
- email: z.string().email('Invalid email'),
629
- otp: z.string().min(6, 'OTP must be 6 digits'),
630
- });
631
-
632
- export const UnitSchema = z.object({
633
- name: z.string().min(2, 'Name is required'),
634
- address: z.string().min(5, 'Address is required'),
635
- price: z.number().positive('Price must be positive'),
636
- });
637
- ```
638
-
639
- ```tsx
640
- // components/forms/LoginForm.tsx
641
- import { useForm, Controller } from 'react-hook-form';
642
- import { zodResolver } from '@hookform/resolvers/zod';
643
- import { LoginSchema } from '@/lib/api/schemas';
644
-
645
- export function LoginForm({ onSubmit }: { onSubmit: (data: any) => void }) {
646
- const { control, handleSubmit, formState: { errors } } = useForm({
647
- resolver: zodResolver(LoginSchema),
648
- });
649
-
650
- return (
651
- <View>
652
- <Controller
653
- control={control}
654
- name="email"
655
- render={({ field: { onChange, onBlur, value } }) => (
656
- <>
657
- <TextInput
658
- placeholder="Email"
659
- value={value}
660
- onChangeText={onChange}
661
- onBlur={onBlur}
662
- keyboardType="email-address"
663
- style={{ borderWidth: 1, padding: 12, marginBottom: 8 }}
664
- />
665
- {errors.email && <Text style={{ color: 'red' }}>{errors.email.message}</Text>}
666
- </>
667
- )}
668
- />
669
- <TouchableOpacity onPress={handleSubmit(onSubmit)}>
670
- <Text>Submit</Text>
671
- </TouchableOpacity>
672
- </View>
673
- );
674
- }
675
- ```
676
-
677
- ## Performance Optimization
678
-
679
- ### Use FlashList Instead of FlatList
680
-
681
- ```tsx
682
- import { FlashList } from '@shopify/flash-list';
683
-
684
- <FlashList
685
- data={units}
686
- renderItem={({ item }) => <UnitCard unit={item} />}
687
- keyExtractor={(item) => item.id}
688
- estimatedItemSize={100}
689
- />
690
- ```
691
-
692
- ### Image Optimization with expo-image
693
-
694
- ```tsx
695
- import { Image } from 'expo-image';
696
-
697
- <Image
698
- source={require('@/assets/image.png')}
699
- style={{ width: 200, height: 200 }}
700
- contentFit="cover"
701
- transition={200}
702
- />
703
- ```
704
-
705
- ### Avoid Re-renders
706
-
707
- ```tsx
708
- const MemoizedCard = React.memo(({ unit }: { unit: Unit }) => (
709
- <UnitCard unit={unit} />
710
- ));
711
-
712
- const handlePress = useCallback(() => {
713
- navigation.navigate('details', { id });
714
- }, [id, navigation]);
715
- ```
716
-
717
- ### Loading States (Not Spinners)
718
-
719
- ```tsx
720
- // Use skeleton loaders instead of spinners
721
- <SkeletonLoader height={100} width="100%" count={3} />
722
- ```
723
-
724
- ## Testing
725
-
726
- ### Unit & Component Tests
727
-
728
- ```tsx
729
- // Use Jest + React Native Testing Library
730
- import { render, screen } from '@testing-library/react-native';
731
- import { LoginForm } from '@/components/forms/LoginForm';
732
-
733
- describe('LoginForm', () => {
734
- it('renders email input', () => {
735
- render(<LoginForm onSubmit={jest.fn()} />);
736
- expect(screen.getByPlaceholderText('Email')).toBeTruthy();
737
- });
738
- });
739
- ```
740
-
741
- ### E2E Tests
742
-
743
- Use Detox or Maestro for end-to-end testing on iOS/Android simulators.
744
-
745
- ## Common Gotchas
746
-
747
- ### Platform-Specific Code
748
-
749
- ```tsx
750
- import { Platform } from 'react-native';
751
-
752
- // Option 1: Platform check
753
- {Platform.OS === 'ios' && <View>{/* iOS only */}</View>}
754
-
755
- // Option 2: Platform extensions
756
- // Button.ios.tsx, Button.android.tsx
757
- // import Button from '@/components/Button'
758
- ```
759
-
760
- ### SafeAreaView for Notch/Dynamic Island
761
-
762
- ```tsx
763
- import { SafeAreaView } from 'react-native-safe-area-context';
764
-
765
- <SafeAreaView style={{ flex: 1 }}>
766
- {/* Content */}
767
- </SafeAreaView>
768
- ```
769
-
770
- ### Keyboard Handling
771
-
772
- ```tsx
773
- import { KeyboardAvoidingView } from 'react-native';
774
-
775
- <KeyboardAvoidingView behavior="padding" style={{ flex: 1 }}>
776
- {/* Form content */}
777
- </KeyboardAvoidingView>
778
- ```
779
-
780
- ### Android Back Button
781
-
782
- ```tsx
783
- useEffect(() => {
784
- const backAction = () => {
785
- // Handle back navigation
786
- return true; // Return true to prevent default behavior
787
- };
788
-
789
- const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
790
- return () => backHandler.remove();
791
- }, []);
792
- ```
793
-
794
- ### iOS Swipe-to-Go-Back
795
-
796
- ```tsx
797
- // Disable in specific screens if needed
798
- useNavigationOptions({
799
- gestureEnabled: false,
800
- });
801
- ```
802
-
803
- ## EAS Build & Submit (App Store Deployment)
804
-
805
- ### Setup EAS
806
-
807
- ```bash
808
- # Install EAS CLI
809
- npm install -g eas-cli
810
-
811
- # Log in to Expo account
812
- eas login
813
-
814
- # Initialize EAS in your project
815
- eas build:configure
816
- ```
817
-
818
- This creates `eas.json` with build profiles:
819
-
820
- ```json
821
- {
822
- "cli": { "version": ">= 3.0.0" },
823
- "build": {
824
- "development": {
825
- "developmentClient": true,
826
- "distribution": "internal"
827
- },
828
- "preview": {
829
- "distribution": "internal"
830
- },
831
- "production": {}
832
- },
833
- "submit": {
834
- "production": {
835
- "ios": { "ascAppId": "YOUR_APP_STORE_CONNECT_APP_ID" },
836
- "android": { "track": "production" }
837
- }
838
- }
839
- }
840
- ```
841
-
842
- ### Building for App Stores
843
-
844
- ```bash
845
- # iOS production build
846
- eas build --platform ios --profile production
847
-
848
- # Android production build
849
- eas build --platform android --profile production
850
-
851
- # Both platforms
852
- eas build --platform all --profile production
853
- ```
854
-
855
- ### Code Signing
856
-
857
- **iOS:**
858
- - EAS handles provisioning profiles and certificates automatically
859
- - First build prompts to log in to Apple Developer account
860
- - Choose "Let EAS handle it" for automatic management
861
-
862
- **Android:**
863
- - EAS generates and manages the keystore automatically
864
- - First build creates a keystore stored securely on EAS servers
865
- - For existing keystores: `eas credentials` to upload
866
-
867
- ### Submitting to App Stores
868
-
869
- ```bash
870
- # Submit to Apple App Store (requires App Store Connect setup)
871
- eas submit --platform ios --latest
872
-
873
- # Submit to Google Play Store (requires Google Play Console setup)
874
- eas submit --platform android --latest
875
-
876
- # Submit specific build
877
- eas submit --platform ios --id BUILD_ID
878
- ```
879
-
880
- ### Pre-submission Checklist
881
-
882
- - [ ] App icon (1024x1024 for iOS, 512x512 for Android)
883
- - [ ] Splash screen configured
884
- - [ ] `app.json` has correct bundle identifier and version
885
- - [ ] Privacy policy URL set (required for both stores)
886
- - [ ] App Store screenshots prepared (iPhone, iPad if applicable)
887
- - [ ] Google Play feature graphic (1024x500)
888
- - [ ] App description and keywords
889
- - [ ] Age rating questionnaire completed
890
- - [ ] In-app purchases configured (if applicable)
891
-
892
- ### OTA Updates (Over-The-Air)
893
-
894
- For JS-only updates without rebuilding:
895
-
896
- ```bash
897
- # Push an update to production
898
- eas update --branch production --message "Fix: corrected button alignment"
899
-
900
- # Push to preview
901
- eas update --branch preview --message "Feature: new onboarding flow"
902
- ```
903
-
904
- Configure in `app.json`:
905
- ```json
906
- {
907
- "expo": {
908
- "updates": {
909
- "url": "https://u.expo.dev/YOUR_PROJECT_ID"
910
- },
911
- "runtimeVersion": { "policy": "appVersion" }
912
- }
913
- }
914
- ```
915
-
916
- ## Dependencies
917
-
918
- ### Core
919
- - `expo@52+`
920
- - `expo-router` (file-based navigation)
921
- - `react-native` (via Expo)
922
- - `typescript`
923
-
924
- ### State & Data
925
- - `@tanstack/react-query`
926
- - `zustand` (optional, for complex client state)
927
-
928
- ### Forms
929
- - `react-hook-form`
930
- - `@hookform/resolvers`
931
- - `zod`
932
-
933
- ### API
934
- - `axios`
935
-
936
- ### Storage
937
- - `expo-secure-store` (tokens, secrets)
938
-
939
- ### Notifications
940
- - `expo-notifications`
941
- - `expo-device`
942
-
943
- ### Auth
944
- - `expo-local-authentication` (biometric unlock)
945
-
946
- ### Images
947
- - `expo-image`
948
-
949
- ### Lists
950
- - `@shopify/flash-list`
951
-
952
- ### Testing
953
- - `jest`
954
- - `@testing-library/react-native`
955
- - `detox` or `maestro` (E2E)
956
-
957
- ### Dev
958
- - `@types/react-native`
959
- - `@types/react`
960
- - `expo-dev-client` (for custom dev builds)
961
-
962
- ## Quick Start
963
-
964
- ```bash
965
- npx create-expo-app my-app --template
966
- cd my-app
967
- npx expo install expo-router
968
- npx expo customize tsconfig
969
- npx expo install axios zustand @tanstack/react-query zod react-hook-form @hookform/resolvers
970
- npx expo install expo-secure-store expo-notifications expo-device expo-local-authentication expo-image @shopify/flash-list react-native-safe-area-context
971
-
972
- # Create basic structure
973
- mkdir -p app/(auth)/(tabs) app/(public) components/{ui,forms} lib/{api,auth,storage} hooks constants
974
-
975
- # Start dev server
976
- npx expo start
977
- ```