Changelog
Notable changes to NativeExpress, grouped by release.
The format follows Keep a Changelog . Releases use Semantic Versioning .
2.1.2 - 2026-10-05
NativeExpress 2.1.2 stops anyone from listing the avatars bucket, which exposed every user ID, and fixes two chat bugs and a set of smaller issues.
Upgrading from 2.1.1
Run upgrade.sql from the release
in the Supabase SQL editor of every project built from NativeExpress, development
projects included. It replaces the “Anyone can view avatars” storage policy with “Users
can read their own avatar” and is safe to run twice. Avatars keep loading in installed
app versions, because a public bucket serves files by URL without checking a policy.
There is no other database change and no new native module. The release’s README.md
lists the files to merge with care.
Fixed
- Avatar storage could be listed by anyone with the app’s public key. File names are
user IDs. Nobody can list the bucket now, and
yarn check:dbasserts it. - Reopening a chat thread dropped the last exchange.
- On iPad, the first reply of a new chat never appeared.
- A failed font load left the app on the splash screen. It now opens with system fonts.
- The Edit Profile sheet sat under the keyboard.
- Expired auth links written with two slashes (
nativeexpress://update-password#error=…) opened Update password. They now end on Sign in with a message, like the three-slash form. - Edge-function and chat requests sent while signed out carried
Authorization: Bearer undefined. They now fail before sending, with401 unauthorized. - The deep-link listener re-subscribed on every render.
Changed
- Any ASCII symbol counts as a password’s special character, the same set Supabase uses when a project requires symbols. The length message matches the 64-character limit.
- Sign-in fields carry autofill hints (
autoComplete,textContentType), and the show-password button is labelled for screen readers.
2.1.1 - 2026-10-02
NativeExpress 2.1.1 makes the app build and launch with Xcode 27, removes Android permissions Google Play rejects, and prepares the edge functions for Supabase’s new API keys.
Upgrading from 2.1.0
There is no database migration. Follow Update the codebase,
run yarn install and rebuild the native app. The iOS and Android changes are native
configuration, so restarting the dev server does not apply them. Plan for these changes:
- Merge
app.config.jsandpackage.jsontogether, then runnpx expo prebuild --cleanor start a new EAS build.enableSceneSupportneedsexpo57.0.23 andexpo-build-properties57.0.20 or newer;npx expo install --fixbrings both up. EAS’s default image for SDK 57 still builds with Xcode 26, where the app launched before this release too. You need the change now if you build with Xcode 27, and before April 2027 either way. See Xcode 27 and the iOS 27 SDK. - Set
EXPO_PUBLIC_ONE_SIGNAL_APP_IDin every EAS environment that builds an app with push. The OneSignal plugin is registered only when it is set. See Push notifications. - Replace a legacy Supabase
anonkey in order. Redeploy the functions, release a build with the publishable key, and only then disable the legacy keys. Installed builds keep the key they were compiled with. See Replace the legacy Supabase keys, and troubleshooting if a function answersInvalid JWTafter you rotate to asymmetric signing keys. - Give every new
publictable explicit grants. Supabase stops granting access to new tables on existing projects from 30 October 2026. Tables you already have keep their grants. See Grant access to a new table. - Check the Android permissions of your next release in Play Console. If you read the photo library yourself, keep the permissions you need and file Play’s photo and video declaration. See Image generation.
- Push the age rating again if your app has no feed, public profiles or messages
between users. Set
user-generated-content,messaging-and-chatandsocial-mediatofalsein yourdeclarations.config.json, then runnode .claude/skills/submit/scripts/declarations.mjs --push. Answers already pushed stay in App Store Connect until you push new ones. See Age rating and content rights. - Add the account-deletion web link for Google Play. Record it as
playConsoleOnly.accountDeletionUrlinplay.config.jsonand enter it in the Data safety form. See the account-deletion link. - Use Node.js 22.13 or later, which is Expo SDK 57’s own minimum.
Changed
- The Android manifest no longer declares
READ_MEDIA_IMAGES,READ_MEDIA_VIDEOorREAD_MEDIA_AUDIO. The app saves images and picks them through the system photo picker, so it never needed them, and Google Play only accepts them from apps whose core use requires them. - The edge functions prefer Supabase’s publishable and secret keys and fall back to the
legacy
anonandservice_rolekeys, which Supabase is removing by the end of 2026.yarn skills:doctornames theLegacy API keys are disabledfailure. declarations.config.jsonanswers Apple’s social media question, required on every submission since September 2026. It no longer declares user-generated content or messaging for an app whose only chat is with a model.- The OneSignal plugin is registered only when
EXPO_PUBLIC_ONE_SIGNAL_APP_IDis set, and its notification extension uses the app’s own deployment target. OnboardingHeaderandProgressDotsmoved fromsrc/apptosrc/components/onboarding, because Expo Router registered them as routes.yarn check:routesnow fails on any file undersrc/appwithout a default export.- The setup and submit skills cover explicit grants for new tables, the account-deletion web link Google Play requires, and the Google Sign-In version pin on Android.
- Expo packages are updated to the current SDK 57 patch releases. The app requires Node.js 22.13 or later.
Fixed
- Apps built with Xcode 27 no longer crash at launch. The iOS 27 SDK requires the UIKit
scene lifecycle, which the app now adopts through
expo-build-properties. - The project builds with Xcode 27. The OneSignal notification extension was created for iOS 11, which Xcode 27 refuses.
- The avatar picker no longer opens behind the Edit Profile drawer.
- The chat attach drawer no longer opens under the keyboard.
Known issues
- Picking a photo without cropping crashes on iOS 27.1 in
expo-image-picker(expo/expo#50435 ). The fix is in Expo SDK 58 only. The avatar picker crops, so it is not affected; chat attachments and scan are. See troubleshooting. - Google Play requires an in-app way to report AI-generated content. The app does not have one yet.
2.1.0 - 2026-10-01
NativeExpress 2.1 lets a coding agent set the app up from a product brief, and adds scripts that remove what you do not need, generate the design, and submit to both stores. AI features now ask for the user’s consent before sending anything.
Upgrading from 2.0.1
There is no database migration. Follow Update the codebase and plan for three changes:
- Release the app update before deploying the edge functions. The new
chat,generateandidentifyfunctions refuse AI requests from users who have not allowed AI processing, and a 2.0.x build has no way to ask. Once they are deployed, chat, create and scan stop working in older builds, so deploy the functions only after the update is live. eas.jsonnames the App Store Connect key.submit.production.iosgainsascApiKeyPath,ascApiKeyIdandascApiKeyIssuerId, set to"$EXPO_ASC_API_KEY_PATH","$EXPO_ASC_KEY_ID"and"$EXPO_ASC_ISSUER_ID". Copy them as they are, and runsource credentials/asc.envbeforeeas buildandeas submit. See Store credentials.paywall_showncounts drop. It now fires only when a paywall was on screen, and every other paywall request sendspaywall_skipped. Adjust charts and funnels built onpaywall_shown.
Added
PRODUCT.md, the product brief, and a discovery phase at the start of the setup skill. One interview writes the brief, derives the app identity from the app name and Expo account username, and removes the features and integrations the brief marks out.yarn skills:doctorreports the brief’s state.yarn remove:feature <chat|create|scan>andyarn remove:integration <sentry|onesignal|posthog>, both with--dry-run.yarn check:feature-variantsandyarn check:integration-variantsprove the results in CI. See Removing features and integrations.- A
designskill. Themes are generated from seeds insrc/theme/seeds/(mono,paper,slate) withyarn theme:generate;yarn theme:fontsswaps the font pair;yarn theme:iconbuilds the icon, adaptive icon and splash from a logo;yarn check:themeruns in CI. See Design. - A guided EAS path:
eas init --account <owner> --non-interactive, an App Store Connect API key incredentials/asc.envas the iOS credential route, and a reference for everyeas buildandeas submitprompt. The doctor reports the EAS project, and the submit preflight warns about an icon with an alpha channel. nativeexpress create-appasks only for the app name and Expo account username, derives the slug, scheme and identifiers, accepts--ownerand-y, and prints the prompt that starts the setup skill’s discovery phase.- A store submission pipeline covering both stores from the terminal. The App Store side
runs on
asc, the App Store Connect CLI, for the version record, listing text, screenshots per display slot, age rating, content rights, attaching the build, TestFlight and review submission. The Play side is a dependency-free client against the Play Developer API for the listing, track promotion with staged rollout, and review replies. See Submit to the stores. privacy.config.json, one privacy declaration for both stores. It is pruned to the features and integrations the build still ships, then mapped onto Apple’s App Privacy vocabulary and Google’s Data safety vocabulary.--pushrefuses to run until a human has read both tables and sethumanConfirmed, and the scripts hard-fail rather than publish “no data collected” for an app with accounts. Apple has no public API for this, so it goes through an Apple ID web session; Google’sapplications.dataSafetyis a real endpoint and takes the Play Console CSV, which is written to disk on every run either way. See Privacy declarations.declarations.config.jsonfor age rating and content rights. Content rights ships unanswered on purpose, because declaring third-party content asserts you hold the rights to it. See Age rating and content rights.play.config.json, the Play listing plus aplayConsoleOnlyblock recording the App content answers Google exposes no API for, so they are decided in the repo rather than improvised.preflight.mjslists whichever are still open.- The App Store Connect
.p8now belongs outside the repo, at~/.appstoreconnect/, with only its path in the gitignoredcredentials/asc.env. See Store credentials. - The setup skill creates Superwall’s apps,
proentitlement, products and the two placement campaigns with Superwall’s CLI once you have runsuperwall login, andyarn skills:doctorchecks that each key matches an app and that every placement’s campaign shows a paywall. A campaign the CLI creates holds out everyone until you attach a paywall, so add the Superwall keys last. See Superwall. - AI consent, for App Store Guideline 5.1.2(i). Chat, create and scan show an inline
card naming what they send and the companies that receive it, OpenRouter and each
model’s provider, and send nothing until the user taps Allow. The permission is
stored on the account, withdrawn from Profile, and enforced by the
chat,generateandidentifyfunctions, which refuse403 ai_consent_requiredwithout it. BumpconsentVersioninsupabase/functions/_utils/ai.config.jsonto ask everyone again.ai_consent_grantedandai_consent_withdrawnare new analytics events. See AI consent.
Changed
- Sentry is behind a crash-reporting port in
src/services/crash-reporting/.src/lib/logger.tsreports through the port, andmetro.config.jsuses Sentry’s Metro wrapper only when the package is installed. DESIGN.md§1 is written per app by the design skill; §2 to §7 are the unchanged contract.app.config.jsreads the icon, splash and canvas colours fromsrc/assets/images/branding.json.- The setup skill’s identity phase no longer asks for identifiers, and the skill hands over to the design skill once the app runs.
paywall_showncounts will drop. The event used to fire on every paywall request, including requests that showed nothing; it now fires only when a paywall was on screen. The difference arrives as the newpaywall_skippedevent, withplacementand areasonsuch asunconfiguredorholdout. Requests are the sum of the two. With Superwall, a campaign’s paywall on screen counts as shown, and a holdout, unmatched audience or missing placement as skipped. See Analytics usage.- Onboarding completion moved out of the notifications screen into
useCompleteOnboarding, and screen order intoONBOARDING_STEPSinsrc/lib/onboarding.ts. The last listed step completes onboarding, so rebuilding that screen no longer drops the saved answers,onboarding_completedor theonboarding_endpaywall. Screens no longer pass step numbers:OnboardingHeadertakes only an optionalonSkip.onboarding_completed’snotificationsproperty and the savednotificationsanswer are optional. See Onboarding. yarn remove:integration onesignalalso removes the notifications onboarding step, so onboarding ends at personalize.- Sentry reports performance traces from every development session and 20% of release sessions, attaches the signed-in user’s id but never the email, strips query strings from request URLs, and reports render errors that reach Expo Router’s error boundaries. Session replay is off, in Sentry and in PostHog. See Monitoring.
yarn skills:doctor, the verify scripts, the submit scripts and the theme and removal commands take--help. They reject an unknown or misspelt flag and exit 2 on a usage error instead of ignoring it.yarn cialso runsyarn check:submitandyarn check:scripts, thenode:testsuites for the submit scripts andscripts/.
Fixed
- The repository works on Windows.
.gitattributeschecks text files out with LF line endings, which the checks compare byte for byte.yarn check:docsandyarn check:i18ncompare paths correctly, andyarn skills:doctorstarts theeasandsupabaseCLIs instead of reporting them as signed out. - A failed onboarding completion shows an error toast and lets the user try again.
- A failed profile save keeps the edit drawer open with the picked photo instead of closing as if it had worked.
- Onboarding’s Skip is a real button, with a button role for screen readers and a larger touch target.
2.0.1 - 2026-09-05
NativeExpress 2.0.1 makes Chat, Create and Scan independently removable and fixes quota enforcement, attachment access, purchases and account cleanup.
Upgrading from 2.0.0
New projects can use this release directly. For an existing app, follow the README.md
attached to the release
and plan for three changes:
- Review
upgrade.sqlagainst your schema. It is a separate release asset and is not part of the boilerplate’s migration history. Do not reapply the boilerplate’s initial migration to an existing database. - Replace
EXPO_PUBLIC_SUPERWALL_KEYwithEXPO_PUBLIC_SUPERWALL_API_KEY_APPLEandEXPO_PUBLIC_SUPERWALL_API_KEY_GOOGLE, using the public SDK key for each platform. Update local and EAS environments before rebuilding. - Preserve your customizations when merging. Feature code moved into
src/features/, and model settings moved from the shared AI configuration to each edge function’sconfig.json.
Changed
- Each feature owns its screens, hooks, data access and model configuration. Home and
tabs use a shared registration list.
src/features/README.mdhas the removal instructions. - The boilerplate still ships exactly one initial migration, with separate SQL sections for each feature.
- CI verifies Chat-only, Create-only, Scan-only and empty-shell projects with the other features physically removed.
Fixed
- Chat and Scan consume quota atomically. Concurrent requests cannot exceed the free allowance, and deleting history cannot refund usage. Admitted attempts count even when the provider fails. Create remains Pro-only.
- Chat verifies ownership of stored attachments, rejects exhausted accounts before downloading media and bounds image context. Clients can no longer insert or update AI records directly.
- Superwall purchases and restores use RevenueCat and wait for native entitlement synchronization before reporting success. Android purchases select the requested plan and offer.
- Account changes clear purchase and push identity state and reject stale purchase results.
2.0.0 - 2026-09-05
NativeExpress 2.0 adds chat, image generation and scanning, with Uniwind, Tailwind CSS v4 and HeroUI Native for styling and components.
Breaking changes
The styling system, component library, AI functions and database schema have changed. For an existing app built on NativeExpress 1.x, follow Upgrade from v1.
Added
- Streaming AI chat with threads, a model picker, Markdown replies and a monthly free quota. Conversations support photo attachments and image replies.
- An image-generation screen with style presets, aspect ratios, a gallery and save/share actions.
- A camera-based object scanner with structured results and a scan history.
- A three-step onboarding flow with a notification primer and named paywall placements.
Answers are saved locally, then attached to
user_metadata.onboardingafter sign-in. Existing account answers are preserved, and a failed sync keeps the local record. - Eleven typed product events covering onboarding, authentication, AI features,
paywalls and Pro status. Their property types are defined in
src/services/analytics/events.ts. - A check-your-inbox screen and resend action for sign-ups that require email confirmation.
- Optional Superwall paywall presentation alongside RevenueCat. Purchase handling and subscription synchronization need the additional integration steps.
- iPad layouts with a
tablet:breakpoint, shared content width and a chat list/detail view. - Jest and React Native Testing Library tests, plus Maestro flows under
.maestro/. - Store submission configuration in
store.config.jsonandeas.json, with skills for preparing screenshots, listing assets and release checklists. - A setup skill covering app identity, Supabase, AI functions and optional integrations.
yarn skills:doctorreports configuration and checks remote metadata when access is available. setup,conventions,uniwindandheroui-nativeskills in both.claude/skills/and.agents/skills/.store-assetsandsubmitare included under.claude/skills/.DESIGN.md, the app’s design reference.
Changed
- The app uses Uniwind, Tailwind CSS v4 and HeroUI Native. Theme colors are defined in CSS, with Geist for interface text and Instrument Serif for display text.
- The platform dependencies are Expo 57.0.20, React Native 0.86.3, React 19.2.3 and TypeScript 6.0.3. The app requires Node.js 22 or later.
yarn typecheckgenerates Expo Router route types withnpx expo customize tsconfig.json.tsconfig.jsonenablesverbatimModuleSyntax.- Expo registers the app scheme, and the Google Sign-In plugin registers its URL scheme.
expo-build-propertiesconfigures the modular headers needed by Google’sAppCheckCore. - The Expo Doctor exclusion list is removed.
ScanCameraModalusesuseEffectEventfor callbacks that read current values. PaywallProvidermounts the shared paywall UI and the optional Superwall provider. Paywall presentation is exported from@/services/paywall.- The app and functions share the AI registry in
supabase/functions/_utils/ai.config.json.config.jsreads this file as itsaiblock.yarn check:envchecks configured defaults. - Each EAS build profile selects its
development,previeworproductionenvironment. The production profile uses remote versioning and increments its build number. general.easProjectIdstarts empty.app.config.jsaddsextra.eas.projectIdafter the app is linked to its own Expo project.- One initial migration,
20260904000000_initial_schema.sql, creates the database schema, storage buckets and access policies. It includesgenerations,scansandcount_monthly_usage, with anonymous access to the RPC revoked. - Avatar uploads use
avatars/{userId}.<ext>within the fixedavatarsbucket. Insert and update policies check the user’s ID.EXPO_PUBLIC_SUPABASE_BUCKET_NAMEis removed. - Shared function utilities handle authentication, typed errors, model configuration, entitlements, quota checks and storage.
- Chat sends the newest user message and storage paths for attachments. The function
loads history from the database. Retrying the same pending message reuses its row
without adding usage, but sends another request to the model. Scan requests use
imagePath. - Free usage is counted from stored user messages and scan rows. Chat inserts its user message before calling the model, so a later provider failure can still count. Successful scans add usage after processing. See Quota for the behavior.
- Analytics, push and paywalls have service entry points under
src/services/, with lint rules restricting direct vendor imports. - Auth navigation uses
Stack.Protectedroute guards. Supabase sessions useexpo-secure-store, with existing AsyncStorage sessions migrated on first launch. - Theme preference is saved and follows the operating system until the user selects a mode.
- OneSignal’s APNs mode follows
ONESIGNAL_MODEwhen set, otherwise the EAS build profile. Only the production profile defaults to production APNs.
Fixed
- Chat, Create and Scan show provider-configuration errors. Pro-only requests open the paywall, and failed chat messages have a retry action.
- Small photos retain their dimensions during upload instead of being enlarged to the cap.
- Expired image URLs are refreshed in chat, the gallery, the home screen and scan history.
- RevenueCat identity and logout calls are skipped until its platform key is configured. Missing purchase configuration produces a notice when the user opens a paywall.
- Account deletion removes the user’s avatar and chat-media objects before deleting the auth user.
- Functions return typed error codes while keeping detailed errors in their logs.
Validation and provider-configuration refusals before message insertion do not add
chat usage. Invalid
AI_FREE_MESSAGESvalues fall back to the configured default. - The database-status scripts use
supabase migration listfor local and linked projects. - Pro checks use the configured RevenueCat entitlement on the client and server.
- Chat streams replies incrementally through
expo/fetch. - Chat and Scan report camera or photo-library access failures.
- Screen titles, content gutters and safe-area backgrounds are consistent across tabs.
- RevenueCat offering-load failures are handled without an unhandled rejection.
- Push permission is requested through onboarding.
- The displayed monthly usage refreshes after AI requests.
- Relative timestamps use APIs available in Hermes.
Removed
- GlueStack UI, NativeWind,
@legendapp/motion,@expo/vector-iconsand@supabase-cache-helpers/postgrest-react-query. src/components/ui/,gluestack-ui.config.jsonandtailwind.config.js.- The demo product screens, joke generator and
call-llmfunction. - Bring-your-own-key support.
seed.sql; database seeding is disabled insupabase/config.toml..cursorrules; the repository includesAGENTS.mdfor coding agents.