Skip to Content

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:db asserts 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, with 401 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.js and package.json together, then run npx expo prebuild --clean or start a new EAS build. enableSceneSupport needs expo 57.0.23 and expo-build-properties 57.0.20 or newer; npx expo install --fix brings 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_ID in 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 anon key 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 answers Invalid JWT after you rotate to asymmetric signing keys.
  • Give every new public table 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-chat and social-media to false in your declarations.config.json, then run node .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.accountDeletionUrl in play.config.json and 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_VIDEO or READ_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 anon and service_role keys, which Supabase is removing by the end of 2026. yarn skills:doctor names the Legacy API keys are disabled failure.
  • declarations.config.json answers 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_ID is set, and its notification extension uses the app’s own deployment target.
  • OnboardingHeader and ProgressDots moved from src/app to src/components/onboarding, because Expo Router registered them as routes. yarn check:routes now fails on any file under src/app without 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, generate and identify functions 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.json names the App Store Connect key. submit.production.ios gains ascApiKeyPath, ascApiKeyId and ascApiKeyIssuerId, set to "$EXPO_ASC_API_KEY_PATH", "$EXPO_ASC_KEY_ID" and "$EXPO_ASC_ISSUER_ID". Copy them as they are, and run source credentials/asc.env before eas build and eas submit. See Store credentials.
  • paywall_shown counts drop. It now fires only when a paywall was on screen, and every other paywall request sends paywall_skipped. Adjust charts and funnels built on paywall_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:doctor reports the brief’s state.
  • yarn remove:feature <chat|create|scan> and yarn remove:integration <sentry|onesignal|posthog>, both with --dry-run. yarn check:feature-variants and yarn check:integration-variants prove the results in CI. See Removing features and integrations.
  • A design skill. Themes are generated from seeds in src/theme/seeds/ (mono, paper, slate) with yarn theme:generate; yarn theme:fonts swaps the font pair; yarn theme:icon builds the icon, adaptive icon and splash from a logo; yarn check:theme runs in CI. See Design.
  • A guided EAS path: eas init --account <owner> --non-interactive, an App Store Connect API key in credentials/asc.env as the iOS credential route, and a reference for every eas build and eas submit prompt. The doctor reports the EAS project, and the submit preflight warns about an icon with an alpha channel.
  • nativeexpress create-app asks only for the app name and Expo account username, derives the slug, scheme and identifiers, accepts --owner and -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. --push refuses to run until a human has read both tables and set humanConfirmed, 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’s applications.dataSafety is a real endpoint and takes the Play Console CSV, which is written to disk on every run either way. See Privacy declarations.
  • declarations.config.json for 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 a playConsoleOnly block recording the App content answers Google exposes no API for, so they are decided in the repo rather than improvised. preflight.mjs lists whichever are still open.
  • The App Store Connect .p8 now belongs outside the repo, at ~/.appstoreconnect/, with only its path in the gitignored credentials/asc.env. See Store credentials.
  • The setup skill creates Superwall’s apps, pro entitlement, products and the two placement campaigns with Superwall’s CLI once you have run superwall login, and yarn skills:doctor checks 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, generate and identify functions, which refuse 403 ai_consent_required without it. Bump consentVersion in supabase/functions/_utils/ai.config.json to ask everyone again. ai_consent_granted and ai_consent_withdrawn are new analytics events. See AI consent.

Changed

  • Sentry is behind a crash-reporting port in src/services/crash-reporting/. src/lib/logger.ts reports through the port, and metro.config.js uses 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.js reads the icon, splash and canvas colours from src/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_shown counts 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 new paywall_skipped event, with placement and a reason such as unconfigured or holdout. 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 into ONBOARDING_STEPS in src/lib/onboarding.ts. The last listed step completes onboarding, so rebuilding that screen no longer drops the saved answers, onboarding_completed or the onboarding_end paywall. Screens no longer pass step numbers: OnboardingHeader takes only an optional onSkip. onboarding_completed’s notifications property and the saved notifications answer are optional. See Onboarding.
  • yarn remove:integration onesignal also 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 ci also runs yarn check:submit and yarn check:scripts, the node:test suites for the submit scripts and scripts/.

Fixed

  • The repository works on Windows. .gitattributes checks text files out with LF line endings, which the checks compare byte for byte. yarn check:docs and yarn check:i18n compare paths correctly, and yarn skills:doctor starts the eas and supabase CLIs 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.sql against 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_KEY with EXPO_PUBLIC_SUPERWALL_API_KEY_APPLE and EXPO_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’s config.json.

Changed

  • Each feature owns its screens, hooks, data access and model configuration. Home and tabs use a shared registration list. src/features/README.md has 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.onboarding after 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.json and eas.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:doctor reports configuration and checks remote metadata when access is available.
  • setup, conventions, uniwind and heroui-native skills in both .claude/skills/ and .agents/skills/. store-assets and submit are 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 typecheck generates Expo Router route types with npx expo customize tsconfig.json. tsconfig.json enables verbatimModuleSyntax.
  • Expo registers the app scheme, and the Google Sign-In plugin registers its URL scheme. expo-build-properties configures the modular headers needed by Google’s AppCheckCore.
  • The Expo Doctor exclusion list is removed. ScanCameraModal uses useEffectEvent for callbacks that read current values.
  • PaywallProvider mounts 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.js reads this file as its ai block. yarn check:env checks configured defaults.
  • Each EAS build profile selects its development, preview or production environment. The production profile uses remote versioning and increments its build number.
  • general.easProjectId starts empty. app.config.js adds extra.eas.projectId after 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 includes generations, scans and count_monthly_usage, with anonymous access to the RPC revoked.
  • Avatar uploads use avatars/{userId}.<ext> within the fixed avatars bucket. Insert and update policies check the user’s ID. EXPO_PUBLIC_SUPABASE_BUCKET_NAME is 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.Protected route guards. Supabase sessions use expo-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_MODE when 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_MESSAGES values fall back to the configured default.
  • The database-status scripts use supabase migration list for 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-icons and @supabase-cache-helpers/postgrest-react-query.
  • src/components/ui/, gluestack-ui.config.json and tailwind.config.js.
  • The demo product screens, joke generator and call-llm function.
  • Bring-your-own-key support.
  • seed.sql; database seeding is disabled in supabase/config.toml.
  • .cursorrules; the repository includes AGENTS.md for coding agents.
Last updated on