Skip to content

feat(Expo): add config plugin for managed / prebuild workflow#268

Open
BLOCKMATERIAL wants to merge 1 commit into
Rapsssito:masterfrom
BLOCKMATERIAL:feat/expo-config-plugin
Open

feat(Expo): add config plugin for managed / prebuild workflow#268
BLOCKMATERIAL wants to merge 1 commit into
Rapsssito:masterfrom
BLOCKMATERIAL:feat/expo-config-plugin

Conversation

@BLOCKMATERIAL
Copy link
Copy Markdown

Summary

Adds an Expo config plugin so react-native-background-actions works out of the box in Expo managed and prebuild workflows.

The plugin patches AndroidManifest.xml during expo prebuild to:

  • Declare the com.asterinet.react.bgactions.RNBackgroundActionsTask service with the required android:foregroundServiceType attribute.
  • Add the matching FOREGROUND_SERVICE_* runtime permission(s).

Why

Since the 4.1.0 release (#265), users can pass foregroundServiceType at runtime to BackgroundService.start(). However, Android 14+ also requires the service to be declared with android:foregroundServiceType in AndroidManifest.xml, otherwise apps targeting targetSdkVersion >= 34 crash with:

android.app.InvalidForegroundServiceTypeException:
Starting FGS with type none callerApp=... targetSDK=36 has been prohibited

For bare React Native projects this is documented in INSTALL.md — users edit the manifest manually. In Expo projects this is not possible because the native folders are regenerated by expo prebuild. Currently every Expo user has to author their own config plugin (the snippet floating around in issues / Stack Overflow). This PR moves that into the package so the Expo experience is zero-config.

Usage

{
  "expo": {
    "plugins": [
      ["react-native-background-actions", { "foregroundServiceType": "dataSync" }]
    ]
  }
}
  • Accepts a single string or an array (matches the runtime API shape).
  • Defaults to "dataSync" when no props are passed.
  • Validates against the known list and throws on unknown types.
  • Adds the corresponding FOREGROUND_SERVICE_* permission automatically for each type.
  • If the user already declared the service, the plugin updates the existing entry rather than duplicating it.

What's in this PR

  • plugin/build/withBackgroundActions.js — runtime entry (CommonJS, no build step needed at install time).
  • plugin/src/withBackgroundActions.ts — TypeScript source for maintainers / future build pipeline.
  • app.plugin.js — Expo's plugin entry point (auto-resolved by @expo/config-plugins).
  • __tests__/plugin.test.js — 5 smoke tests covering: defaults, single type, multiple types, unknown type rejection, in-place service update.
  • INSTALL.md — new "Using Expo (managed / prebuild workflow)" section.
  • package.json — adds plugin/build and app.plugin.js to files.
  • .gitignore — un-ignores plugin/build/ (the global build/ rule was hiding it).

@expo/config-plugins is not added as a dependency. Expo provides it at prebuild time, and the test file mocks it with jest.mock(..., { virtual: true }) so unit tests don't require installing it.

Test plan

  • yarn ci (lint + declaration:build + checkjs + test) is green.
  • 16/16 tests pass (11 existing + 5 new for the plugin).
  • Manually verified with the menu-scan-ai project (Expo SDK 55, RN 0.83, targetSdkVersion 36) — service is registered correctly in the prebuilt manifest, app no longer crashes on startForeground().

Happy to adjust naming, structure, or split into a separate expo-config-plugin-* package if you'd prefer not to bundle the plugin with the main package.

Adds an Expo config plugin that patches AndroidManifest.xml during
`expo prebuild` so the `RNBackgroundActionsTask` service is declared
with the appropriate `android:foregroundServiceType` and the matching
`FOREGROUND_SERVICE_*` runtime permission.

Without this, Expo-managed apps targeting SDK 34+ crash with
`InvalidForegroundServiceTypeException: Starting FGS with type none ...`
because manual edits to AndroidManifest.xml don't survive prebuild.

Usage:

    {
      "expo": {
        "plugins": [
          ["react-native-background-actions", { "foregroundServiceType": "dataSync" }]
        ]
      }
    }

The plugin accepts a string or array of types and defaults to "dataSync".
Values must match the `foregroundServiceType` array passed to
`BackgroundService.start()` at runtime.

- plugin/build/withBackgroundActions.js: runtime entry (CommonJS)
- plugin/src/withBackgroundActions.ts: TypeScript source
- app.plugin.js: Expo plugin entry point
- __tests__/plugin.test.js: smoke tests (5 cases, all passing)
- INSTALL.md: new "Using Expo" section
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