Skip to content

feat: native biodata generator (fix App Store 4.2 rejection) - #24

Merged
pythonsst merged 4 commits into
mainfrom
feat/native-biodata-app
Jul 24, 2026
Merged

feat: native biodata generator (fix App Store 4.2 rejection)#24
pythonsst merged 4 commits into
mainfrom
feat/native-biodata-app

Conversation

@pythonsst

@pythonsst pythonsst commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Why

App Store submission 1.0 (42) was rejected under Guideline 4.2 – Design – Minimum Functionality: both home-screen buttons opened snapbiodata.com, so the app was a website wrapper. A resubmit of the same binary would fail again — the only durable fix is real native functionality.

What this PR does

Rebuilds SnapBiodata as a fully native, offline biodata generator. No WebViews, no external website.

Core feature

  • Native form — Personal / Family / Contact sections driven by the field schema ported from the web app (src/data/biodata.ts)
  • Photo pickerexpo-image-picker, embedded as a data URL
  • 3 templates — Traditional / Elegant / Modern, with visual thumbnails in the editor
  • Native previewsrc/components/BiodataDocument.tsx renders the document on-screen (crisp, reliable; replaced a WebView that rendered blank on simulator)
  • PDF exportexpo-print renders matching HTML (src/templates/html.ts) to a print-ready PDF on-device; shared via expo-sharing
  • Local drafts — save / reopen / delete via AsyncStorage; fully offline, no sign-up
  • See a sample — first-run users can preview a filled sample biodata

Stability fixes (found while running it on the simulator)

  • babel-preset-expo — required so process.env.EXPO_OS is inlined; without it expo-modules-core fails to init
  • No SafeAreaProvider — its native view emits inset changes via the legacy RCTEventEmitter path, which isn't registered under the new architecture (bridgeless) in RN 0.81 → uncaught-error redbox on every launch. Insets are now fed statically from initialWindowMetrics via SafeAreaInsetsContext.
  • Persist drafts on navigation, not per keystroke (avoids a read-modify-write race that could drop edits)

UI

Branded maroon header, avatar cards, polished empty state, visual template thumbnails, gold-accented sections, sticky action bars, safe-area handled on every screen.

Tooling

  • scripts/run-ios.sh + yarn ios:run[:dev|:staging|:prod] — one command: pods, Metro, boot sim, build + launch

New dependencies

expo-print, expo-sharing, expo-image-picker — Expo modules, autolinked on both platforms. Requires an EAS native rebuild (build number auto-increments via remote versioning).

Verification

  • tsc --noEmit
  • eslint — 0 warnings
  • jest
  • ✅ Ran on iOS simulator: clean launch (no redbox), all screens render, PDF generates + share sheet opens

Before resubmitting to App Review

  • Merge → EAS production build + submit (new binary picks up the native modules)
  • Refresh App Store screenshots to show the native UI
  • Reply to App Review describing the native functionality

🤖 Generated with Claude Code

pythonsst and others added 4 commits July 24, 2026 15:21
The app previously launched with two buttons that both loaded
snapbiodata.com — a website wrapper, which Apple rejected under
Guideline 4.2 (Minimum Functionality).

Rebuild the core experience as a fully native, offline app:
- Native multi-section form (Personal / Family / Contact) driven by the
  ported biodata field schema
- Native photo picker (expo-image-picker) for a profile photo
- Template picker + document preview (Traditional / Elegant / Modern)
  rendered from locally-composed HTML
- Export a print-ready PDF on-device (expo-print) and share via the
  native share sheet (expo-sharing); also print / save-as-PDF
- Save, reopen, and delete drafts locally (AsyncStorage) — no network,
  no sign-up

Also: switch to react-native-safe-area-context (core SafeAreaView is
deprecated in RN 0.81), add iOS NSPhotoLibraryUsageDescription to all
schemes, and add jest mocks so the render test stays green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Saving on every keystroke did a full read-modify-write of the records
list each time, so concurrent writes during fast typing could race and
drop edits. Keep the draft live in memory and persist only on Back /
Preview, which is race-free and avoids the storage churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the app launch cleanly and look great end-to-end.

Launch / stability
- Fix a redbox on every launch: react-native-safe-area-context's
  <SafeAreaProvider> native view emits inset changes through the legacy
  RCTEventEmitter path, which isn't registered as callable under the new
  architecture (bridgeless) in RN 0.81. Feed insets statically from
  initialWindowMetrics via SafeAreaInsetsContext instead — every screen
  keeps correct insets with no native event emitter, so no error.
- Use babel-preset-expo so process.env.EXPO_OS is inlined; without it
  expo-modules-core (expo-print / image-picker) failed to initialise.
- Add SafeAreaProvider initialMetrics earlier; now superseded by the
  static-context approach above.

UI
- Redesign Home: branded maroon header that clears the status bar,
  avatar cards for saved biodata, polished empty state with a
  "See a sample biodata" shortcut, floating Create CTA.
- Redesign Editor: visual template thumbnails, dashed photo picker,
  heading chips, gold-accented section headers, sticky action bar.
- Render the Preview natively (new BiodataDocument component) instead of
  a WebView — the WKWebView rendered blank on simulator and added a
  "web wrapper" smell. The app now ships zero web views; the PDF export
  still uses HTML via expo-print (html.ts).
- All screens handle safe-area insets and status-bar style directly.

Tooling
- scripts/run-ios.sh + yarn ios:run[:dev|:staging|:prod]: one command to
  install pods, free/refresh Metro, boot the sim, and build+launch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review nits so the diff is warning-free:
- EditorScreen: move the template-thumbnail static styles into the
  StyleSheet and pass dynamic colours as variables, clearing all
  react-native/no-inline-styles warnings.
- App.tsx: move the INSETS constant below the import block so all
  imports stay at the top of the module.

No behavioural change; ESLint now reports 0 problems.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pythonsst
pythonsst merged commit a59779b into main Jul 24, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant