Skip to content

feat(expo): add an official config plugin for CNG and EAS Build #302

Description

@chrispader

Summary

react-native-nitro-sqlite works in Expo development builds through autolinking, but its optional native build configuration is currently manual:

  • iOS FTS5/other SQLite flags require a consumer Podfile post_install mutation.
  • Android flags require android/gradle.properties (nitroSqliteFlags).
  • sqlite-vec uses the separate NITRO_SQLITE_VEC=1 / nitroSqliteVec=true switches after the companion package is installed.
  • iOS App Group database storage requires both RNNitroSQLite_AppGroup in Info.plist and the Xcode App Groups entitlement.
  • iOS system SQLite currently requires setting NITRO_SQLITE_USE_PHONE_VERSION=1 while installing pods.

That prevents a complete managed Expo / EAS Build / Continuous Native Generation setup: generated ios and android projects should not need hand-edited Podfiles, Gradle properties, Info.plists, or entitlements.

Expo recommends library-owned config plugins for native setup. A built-in plugin would make the existing supported native capabilities reproducible across local expo prebuild, expo run:*, and EAS Build.

Proposed package surface

Ship a compiled plugin with the core package:

// app.config.ts
export default {
  expo: {
    plugins: [
      [
        'react-native-nitro-sqlite',
        {
          fts5: true,
          sqliteVec: false,
          ios: {
            appGroup: 'group.com.example.shared',
            useSystemSqlite: false,
          },
          sqliteFlags: {
            ios: ['SQLITE_ENABLE_GEOPOLY=1'],
            android: ['-DSQLITE_ENABLE_GEOPOLY=1'],
          },
        },
      ],
    ],
  },
}

Suggested API principles:

  • fts5?: boolean defaults to false; the plugin maps true to SQLITE_ENABLE_FTS5=1 on both platforms. Keep it opt-in: FTS adds binary size/attack surface and is not required by every database consumer.
  • sqliteFlags?: { ios?: string[]; android?: string[] } is additive/advanced. Validate each entry (KEY=VALUE on iOS, -DKEY=VALUE on Android), de-duplicate it, and reject flags that contradict the library's fixed safety/ABI settings. Do not add a broad arbitrary-string escape hatch that can silently break a build.
  • ios?: { appGroup?: string; useSystemSqlite?: boolean }. appGroup writes the existing RNNitroSQLite_AppGroup Info.plist key and adds the exact identifier to the app target's com.apple.security.application-groups entitlement. It must preserve existing groups and fail clearly for malformed identifiers. useSystemSqlite should reproduce the current pod-install setting for the library pod only.
  • sqliteVec?: boolean belongs to the companion react-native-nitro-sqlite-vec package's plugin, not core. The core plugin may detect that the companion is installed and emit a targeted error / documentation link when sqliteVec: true is passed, but should not import or hard-depend on it. The companion plugin can delegate shared flag helpers from core and configure both native switches only when the companion package is resolvable.

The first version should intentionally not expose the podspec's performance_mode: it is hard-coded today and its threading semantics need a separate API/safety decision. Likewise, useSystemSqlite should be explicit and documented as a platform-version/feature trade-off, not enabled implicitly.

Native modifications

iOS

  • Use withInfoPlist for RNNitroSQLite_AppGroup.
  • Use withEntitlementsPlist (and, only if necessary, withXcodeProject) to merge the App Groups entitlement without removing app-owned entries.
  • Use the narrowest safe CocoaPods/Podfile-properties mechanism to apply library-scoped C/C++ preprocessor definitions and NITRO_SQLITE_USE_PHONE_VERSION=1. Do not mutate unrelated targets or overwrite an app's post_install hook.

Android

  • Use withGradleProperties to merge nitroSqliteFlags into the existing property without duplicate -D definitions.
  • The companion plugin owns nitroSqliteVec=true; it must verify the companion package is installed before enabling it.
  • No manifest change is expected for core SQLite/FTS/vector configuration.

Acceptance criteria

  1. The package exposes app.plugin.js and typed plugin options, with the plugin build included in published artifacts and compatible with app.config.js/ts/json configuration.
  2. Re-running prebuild is idempotent: no duplicate flags, plist keys, app groups, Podfile edits, or Gradle properties.
  3. Existing manual configuration remains supported and composes without overrides; conflicts produce actionable errors.
  4. fts5: true produces a build where SELECT sqlite_compileoption_used('ENABLE_FTS5') is true on iOS and Android, and FTS table creation succeeds.
  5. The companion package's plugin reliably enables sqlite-vec on iOS and Android; a build without it leaves the base package vector-free.
  6. ios.appGroup produces both the Info.plist value consumed by NitroSQLite and the matching app-target entitlement, while preserving pre-existing entitlements.
  7. ios.useSystemSqlite produces a build linked with system SQLite and the docs state which bundled capabilities may differ.
  8. Unit tests cover option validation, platform transformations, merging and idempotency. An Expo example/test fixture runs clean prebuild for iOS and Android and checks generated plist/entitlements/Gradle outputs. CI should run that CNG fixture against the supported Expo SDK range.
  9. README documents the declarative setup plus the legacy manual setup, explicit rebuild requirement, and EAS Build behavior.

Prior art / context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions