Home / Product / Consumer App

Affirmology app architecture decision, v1 (native vs wrapper, answered honestly)

Updated Jun 25, 2026 · Affirmology_AppArchitecture_Decision_v1.md

Summary. Status: DECISION DOC for Jeff, written in response to your questions on the overview. This supersedes the "just wrap everything in Capacitor" framing in AffirmologyAppDeliveryPlanv1.md where they conflict. The short version: you are right to want native for th

Affirmology app architecture decision, v1 (native vs wrapper, answered honestly)

Status: DECISION DOC for Jeff, written in response to your questions on the overview. This supersedes the "just wrap everything in Capacitor" framing in Affirmology_AppDelivery_Plan_v1.md where they conflict. The short version: you are right to want native for the end-user app, my "months" line was an overstatement, and the two apps want two different answers.


1. The honest correction (why I said "months," and what is actually true)

I conflated two different jobs.

Also, "native" does not have to mean hand-written Swift. React Native is native (it renders real native UI components, not a webview), but you build it in JavaScript/React, which is fast to produce and close to the web skills we already use. That option is the key to your whole concern, so it gets its own row below.

2. The three real options

There are not two choices (native vs web wrapper). There are three.

Option A: Native Swift / SwiftUI

Option B: React Native (with Expo), the one I recommend for the consumer app

Option C: Capacitor wrapper (our web app inside a native shell)

3. My recommendation, split by app

The two apps have different jobs, so they get different answers.

Atlas (the Studio): keep the web app, wrap it (Option C). Do NOT rebuild native.

Atlas is internal, already built, complex, and allowed to be a little clunky. It is used by you, Sol, Colin, and soon a handful of elevated creators. Spending weeks to re-create the working web Studio in Swift buys you almost nothing. Wrap it so it installs like an app, keep improving the web version (which instantly improves the app), and move on.

Crucially, wrapping now does NOT lock you out of native later. The brains of Atlas (the engine, the agents, the render pipeline, the data, the APIs) live on the SERVER. The app is just a face on top. You can replace that face later (web-wrap today, native tomorrow) without touching the engine. So starting wrapped costs you nothing strategically, and you will probably never need to redo it for an internal tool.

Affirmology consumer app: go React Native (Option B). Your instinct is right.

This app is media-first, has to feel premium, must do offline + background audio, will iterate fast, wants Android eventually, and is going to the public App Store in August. React Native hits every one of those: native feel, the best-in-class audio library for background/offline listening, fast build, one codebase for Android later, and over-the-air updates so you can iterate without waiting on Apple. Pure Swift is the only thing that beats it on raw polish, but at the cost of iOS-only and slower iteration, which is the wrong trade for a fast-moving consumer beta. I would build it in React Native.

4. The thing that actually answers "how fast can we push changes"

This matters more than native-vs-wrapper, and it removes most of your anxiety. There are THREE kinds of "change," at three very different speeds:

  1. CONTENT and DATA: which audios are available, pushing new audios, new categories, descriptions, sound beds, the chatbot's knowledge, reports. This lives on the SERVER. Updates are INSTANT, no app release, no Apple review, no matter native or wrapper. This is MOST of what you want to change. "Swap which audios are pushed every few days" is a data operation, not an app update. You will do it constantly with zero friction.
  2. APP LOGIC and UI tweaks: layout, copy, feature flags, bug fixes. With React Native + over-the-air updates (or a remote-loaded web wrapper), these go straight to phones in minutes, NO Apple review. With pure Swift, these need a new build.
  3. GENUINELY NEW NATIVE FEATURES: a new capability that touches native code. This needs a new binary submitted to Apple. On TestFlight that is instant for internal testers and about a day for the first external build of a version; on the public App Store a full update review is typically about a day.

So: audios and content change daily for free; UI and logic fixes ship over the air without Apple; only true new native features hit a review cycle, and even those are fast on TestFlight. One rule to respect: Apple does not allow hiding a whole new feature and flipping it on over the air to dodge review. Material new features go through a build. Everything else is fast.

This is why React Native is such a good fit: the audios are data (instant), most UI iteration is over-the-air (no review), and only big new native capabilities need a submission.

5. Your specific questions, answered

Bundle ID, what it is and what "confirm" means

A bundle ID is just a unique identifier string for the app inside Apple's system, like ai.affirmology.atlas. The reverse-domain style is a convention, not a requirement: it does NOT have to match a real URL and nothing has to exist at that address. It just has to be unique and namespaced to you. It lives in two places: registered in your Apple Developer account (Identifiers) and set in the app project config. When I asked you to "confirm," I only meant approve the string we will register. It does not exist yet and it is not referencing anything in our current system, so your read ("this is cool to use for the developer account, yes") is exactly correct. The one catch: it is effectively permanent once the app ships (it is the app's identity for TestFlight, push, and purchases), so we pick it once. Proposed: ai.affirmology.atlas and ai.affirmology.app. Approve or tweak.

Remote URL vs bundled assets

Bundled = the app ships with its files inside it (works offline, updates via release or over-the-air). Remote URL = the app loads the live website each time it opens (always current, but needs a connection, and a pure remote-URL app risks Apple's thin-wrapper rejection on the public store). For ATLAS (internal, always online) remote URL is fine and fastest. For the CONSUMER app we are going React Native, so this question mostly dissolves: React Native bundles its own code and updates over the air; the audios still stream/cache from our servers.

In-app purchase and driving upgrades (I gave you outdated info before, correcting it)

First, for the free BETA this is a non-issue; nobody is paying inside the app. For the paid launch:

In-app report viewer

Yes, build it, and it is better than emailing a PDF. Two layers: a beautiful NATIVE in-app READING view that renders the written report (the prose, the oracle voices, the mantras, the chart panels) so people stay in the app and it feels premium and works offline, PLUS a quiet "email me the PDF" button for those who want the document. Do not force them to leave the app to read their report. The native reading view also sidesteps clunky in-app PDF rendering entirely.

What "the 4 to 5 day build" actually means

It means roughly 4 to 5 days of actual BUILDING effort (Claude Code doing the work across our sessions, you steering and reviewing), assuming the backend exists and the scope is tight. It is NOT Apple upload or approval time (that is separate and small: TestFlight processing is 15 to 60 minutes; the one external review is about a day). And it is not "Jeff sits at the computer all day for five days." It is elapsed engineering time. It was always a rough estimate; the real gating items are the backend and the app foundation, not Apple.

Do the starter audios have to exist or be named first? No.

Build the platform DATA-DRIVEN. Categories and journey containers exist in the app; each audio is just a DATA RECORD (title, description, category, which oracle, the audio file, an optional report). You add, swap, rename, reorder, or remove audios any time with NO app release and NO Apple review. So we ship the app shell with empty-but-ready categories, and you populate and change the 5 to 10 audios over the following days, test new beds, swap things out, all as data. The app renders whatever is in each person's catalog; it does not care what the specific audios are. You can decide and re-decide the lineup continuously.

6. Reframing the Affirmology v1 (you were right, it is a TESTING SUITE)

Throwing out the "which exact 7 or 8 audios and how many per person" framing. v1 is not the finished paid client product. It is an INSTRUMENT to get audios, reports, feedback, and chatbot conversations in front of the founding testers so THEY help us decide the real starter kit, the tiers, the upsells, the journeys, and the pricing. We build the platform; the content flows through it and changes constantly.

What v1 needs to DO (the platform), independent of which audios are in it:

And a note on the future shape: eventually you may run TWO apps, one experimental build where the founding crew tests crazy new features pre-release, and one stable public app. We do not build two now; we just keep the architecture clean so splitting later is easy.

7. Timeline to a July 1 to 3 beta (honest)

Your goal: beta in founders' hands around July 1, okay with July 2 or 3 (July 1 you are driving; July 2 to 3 you are at a computer). That is about a week from now (today is June 24), and you also want the full scale-ready backend before the beta. Native (React Native) app PLUS a real backend PLUS content in one week is aggressive but not crazy IF we scope hard.

To make July 2 to 3 realistic, the LEAN v1 cut is: magic-link login, the data-driven library by category, the offline + background player, the scoped oracle chatbot, feedback and ratings, push for "new audio ready," and a basic in-app report reading view. Fast-follows (days later, over the air, no new beta needed): the voting board, the listened-but-unrated nudge automation, a polished usage dashboard, Fish-audio replies, and Android. The backend: build the real system-of-record schema now, but only the surface the lean app needs; layer the analytics niceties right after.

Two honest flags: (1) aim for July 2 to 3, not July 1, since you are driving on the 1st and the first install always wants a human at a keyboard. (2) Even July 2 to 3 is tight; if it slips, it slips to mid-July, and the lever to protect the date is scope, not all-nighters. Starting the backend immediately is what makes the date possible.

Also: Atlas can be in your hands in DAYS (it is a wrap-and-ship), which lets you, Sol, and Colin shake out the TestFlight install experience before the consumer app is ready. Good way to de-risk the install flow.

8. Decisions captured from your message

9. Immediate next steps

  1. Approve the two bundle IDs (ai.affirmology.atlas, ai.affirmology.app) or tell me your preferred strings. This is just you saying "yes, register these."
  2. Confirm the architecture call: Atlas wrapped, consumer app React Native. If you agree, I will write the two build briefs for Claude Code (Atlas wrap + Affirmology React Native v1) mirroring the Atlas brief format, with exact pastes.
  3. Greenlight starting the scale-ready backend now (it is the long pole for the July beta). I will spec its lean v1 surface so it does not balloon past the date.
  4. For Atlas: confirm you want it moved to magic-link login (yes/no). If yes, that build also de-Cloudflare-Accesses Atlas; I will note the exact Cloudflare change in the brief.
  5. Leave the beta-cohort emails for next week as you said; meanwhile you, Sol, and Colin are the first three testers for both apps.

10. Round 2: follow-up answers and decisions (2026-06-24)

Answers to the second round of questions, and the decisions locked from them.

11. Round 3: Atlas goes native too (2026-06-25, Jeff's call, objection withdrawn)

DECISION: Atlas will be REBUILT NATIVE in React Native (same stack as the consumer app), NOT the Capacitor wrap. Jeff overrode the wrap recommendation, and the reasoning holds: the wrap case rested on "do not throw away working software you depend on," but Sol is not really using Atlas and Jeff wants a UI overhaul anyway, so there is little to preserve and the main cost of going native evaporates. Native Atlas in RN is actually cleaner: one stack, one shared design system across both apps, OTA updates on Atlas too. The overnight Capacitor wrap (Brief B) is parked (smallest of the three; magic-link auth + the session gate carry over). The backend and engine are untouched, so this is a new face + a new creation flow, not an engine rebuild.

CENTERPIECE: the audio-making experience moves INTO the Hermes chatbot, and it needs to be strong. A creator talks to Hermes ("make a before-bed audio for Jeff leaning into his Cancer moon"), Hermes runs the council, shows the script, the creator iterates by talking, then says "render it." The rails already exist (/api/hermes + /api/compose + /api/audio); this is a UX layer on existing plumbing, made the primary creation surface.

DESIGN PRINCIPLE (keep while making chat strong): chat-PRIMARY, not chat-ONLY. Conversation drives, but the exact controls (person, named structure, length, render settings) stay reachable in a panel for precision moments. Chat for flow, controls for precision; also eases elevating beta testers into creators (lean on chat, grow into controls).

SEQUENCING: the consumer app stays the July priority (it is the dated one). The native Atlas rebuild is a FAST-FOLLOW that reuses the consumer app's RN design system and components, so much is shared. Keeps the July date safe while Atlas gets its overhaul. (Open: run them more in parallel if Jeff prefers.)

TAHOE: Jeff restarts the Mac tonight to bring up macOS Tahoe (pairs with installing full Xcode). Heads-up captured: after the OS jump, verify the engine venv, ffmpeg, node, CocoaPods, and Xcode-vs-Tahoe version compatibility before the next build push.