Skip to content
Back to skills

React Native Android Bridge Implementation

ASecurity

Implements the Android Kotlin bridge layer for a React-Native MoEngage SDK feature. This is Step 2a of the React-Native feature pipeline — run AFTER plugin-base-feature-implementation has completed. Produces the BridgeHandler, arch bridges (new/old), Package, EventEmitterImpl, PayloadGenerator, Constants, and build.gradle for a new sdk/<featureName> module in the React-Native repo, following the Cards module standard. On completion, asks the user whether to also run react-native-ts-implementa...

  • 18 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 1, 2026
developmenttypescriptgojavakotlinbashreactgitapi

Works with

  • claude code
  • cli
  • api

Security analysis

A100/100

Pro scans all 8 files and shows the line behind each finding

Scanned October 1, 2026

npx -y skills add moengage/React-Native --skill react-native-android-bridge-implementation --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of React Native Android Bridge Implementation?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for React Native Android Bridge Implementation
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/moengage-react-native-android-bridge-implementation/badge)](https://www.skillsdirectory.com/skills/moengage-react-native-android-bridge-implementation)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: react-native-android-bridge-implementation
description: >
  Implements the Android Kotlin bridge layer for a React-Native MoEngage SDK feature.
  This is Step 2a of the React-Native feature pipeline — run AFTER plugin-base-feature-implementation
  has completed. Produces the BridgeHandler, arch bridges (new/old),
  Package, EventEmitterImpl, PayloadGenerator, Constants, and build.gradle for a new
  sdk/<featureName> module in the React-Native repo, following the Cards module standard.
  On completion, asks the user whether to also run react-native-ts-implementation (Step 2b).
  Do NOT use for iOS-only features or JS-only changes.
parameters:
  - name: "ticket_id"
    description: "JIRA ticket ID, e.g. 'MOEN-44072'. Extracted from command text if not supplied."
    optional: true
  - name: "feature_description"
    description: "Natural language description of the feature. E.g. 'JWT authentication parity'."
  - name: "contract_branch"
    description: "Branch in 'mobile-sdk-contracts' with the feature contract. E.g. 'MOEN-44072_jwt_contract'."
  - name: "android_bom_version"
    description: "Target MoEngage Android BOM version. E.g. '2.2.1'."
  - name: "android_plugin_base_pr_url"
    description: "URL of the android-plugin-base PR from plugin-base-feature-implementation."
---

## Overview

Implements the **Android Kotlin bridge** inside the React-Native SDK repo (`React-Native`) for a
MoEngage feature whose plugin-base module already exists in `../android-plugin-base`.

**Prerequisite chain:**

1. `plugin-base-feature-implementation` — creates/extends the Android plugin-base module ✅
2. **`react-native-android-bridge-implementation`** ← you are here
3. `react-native-ts-implementation` — TypeScript models, spec, Handler, public API

**Architecture standard:** Follow the **Cards module pattern** exactly. The Cards module is the
canonical reference for all structural decisions.

**Example files:** Templates are in `examples/` adjacent to this SKILL.md. Read each
template before generating the corresponding file.

---

## Example Files Index

```
examples/
  Constants.kt            ← <rnPackage>/Constants.kt
  BridgeHandler.kt        ← <rnBridgeName>Handler.kt (all methods)
  EventEmitterImpl.kt     ← EventEmitterImpl.kt (nativeToHybrid / event methods only)
  PayloadGenerator.kt     ← PayloadGenerator.kt (nativeToHybrid / event methods only)
  Package.kt              ← MoEngage<featureNameCamel>Package.kt
  NewArchBridge.kt        ← src/newarch/ TurboModule bridge
  OldArchBridge.kt        ← src/oldarch/ ReactMethod bridge
```

---

## Phase 0 — Clarify Inputs

### 0.1 Extract ticket ID

Scan the user's full command for `MOEN-\d+` → **`ticketId`**.
If not found in the command or parameters, ask before proceeding.

---

## Phase 1 — Parse Inputs & Derive All Identifiers

### 1.1 Extract from `contract_branch`

Strip everything up to and including the first `/` or `_MOEN-XXXXX_` prefix:

- `feature/experience_contracts` → **`contractSuffix`** = `experience_contracts`
- `MOEN-44072_jwt_contract` → **`contractSuffix`** = `jwt_contract`

### 1.2 Identifiers table

| Identifier          | Example                           | Rule                                                                   |
|---------------------|-----------------------------------|------------------------------------------------------------------------|
| `ticketId`          | `MOEN-44072`                      | `MOEN-\d+` from raw command or parameter                               |
| `contractSuffix`    | `jwt_contract`                    | branch name after first `/` or `_MOEN-XXXXX_`                          |
| `featureName`       | `jwt`                             | lowercase slug from feature_description                                |
| `featureNameCamel`  | `Jwt`                             | PascalCase of featureName                                              |
| `featureNameUpper`  | `JWT`                             | UPPER_SNAKE of featureName                                             |
| `contractDir`       | `authentication`                  | subdirectory found in contracts `json/` after checkout                 |
| `androidModuleName` | `plugin-base-jwt`                 | `plugin-base-<featureName>` (or the actual module from plugin-base PR) |
| `rnSdkDir`          | `sdk/core`                        | see rule below                                                         |
| `rnPackage`         | `com.moengage.react.jwt`          | `com.moengage.react.<featureName>`                                     |
| `rnBridgeName`      | `MoEngageJwtBridge`               | `MoEngage<featureNameCamel>Bridge`                                     |
| `branchName`        | `feature/MOEN-44072-jwt_contract` | `feature/<ticketId>-<contractSuffix>`                                  |

### 1.3 Resolve `rnSdkDir`

Scan `feature_description` for a framework keyword and map to the existing SDK module directory:

| Keyword in `feature_description`              | `rnSdkDir`                                          |
|-----------------------------------------------|-----------------------------------------------------|
| `core`, `analytics`, `inapps`, or `messaging` | `sdk/core`                                          |
| `cards`                                       | `sdk/cards`                                         |
| `geofence`                                    | `sdk/geofence`                                      |
| `inbox`                                       | `sdk/inbox`                                         |
| `personalize`                                 | `sdk/personalize`                                   |
| none of the above                             | ask the user which SDK module to add the feature to |

Examples:

- `"JWT authentication parity from core"` → `sdk/core`
- `"get clicked cards count"` → `sdk/cards`
- `"start geofence monitoring"` → `sdk/geofence`

---

## Phase 2 — Read Contracts

```bash
cd ../mobile-sdk-contracts
git fetch
git stash
git checkout <contract_branch>
```

1. List `json/hybridToNative/` to identify `contractDir`
    - If no matching directory → list available dirs and ask the user
2. For each `.json` in `json/hybridToNative/<contractDir>/`:
    - Filename (without `.json`) = **method name** (camelCase)
    - File content = **input payload shape**
3. For each `.json` in `json/nativeToHybrid/<contractDir>/`:
    - File content = **event/response payload shape**
4. Read all `.proto` files in `protos/<contractDir>/` for field names and types

### Method classification

| Condition                            | Type                | Android return        | Notes                               |
|--------------------------------------|---------------------|-----------------------|-------------------------------------|
| `hybridToNative` only                | **fire-and-forget** | `Unit`                | Delegates to plugin-base Helper     |
| both directions, JS-initiated        | **promise**         | `Promise` param       | Delegates with listener             |
| `nativeToHybrid` only, SDK-initiated | **event**           | emit via EventEmitter | PayloadGenerator + EventEmitterImpl |

**Build a complete method table before writing any code.**

---

## Phase 3 — Android Bridge Implementation

### 3.1 Create branch

```bash
cd React-Native
git fetch
git checkout -b feature/<ticketId>-<contractSuffix>
```

### 3.2 Check if SDK module already exists

```bash
ls <rnSdkDir>/android/src/main/ 2>/dev/null
```

- Not found → scaffold full module (Steps 3.3–3.9)
- Found → read existing files first, then add only the missing methods

**When a module already exists, some of its existing files (e.g. `sdk/core`) predate the
Cards-based standard** (single `MoEReactBridge.kt`/`MoEReactBridgeHandler.kt`, no `newarch`/
`oldarch` split, no per-module `Package.kt`). In that case follow the file's own existing
pattern instead of forcing the Cards-based file split — read every existing file in the module
before writing anything.

**Append new methods/cases at the bottom of the file, not interleaved among existing ones.**
For every file you extend (`MoEReactBridge.kt`, `MoEReactBridgeHandler.kt`, `Constants.kt`):
add each new method as the last member before the closing brace (or, for `MoEReactBridgeHandler.kt`,
immediately before a trailing `companion object` if one exists), not next to the nearest
similar-looking existing method. For `EventEmitterImpl.kt`'s `when (event.eventType)` block,
add the new case immediately before the `else` branch (not next to a similar existing case);
add the new `emitXxx()` private function as the last function in the class; add the new
`EventType to "..."` pair as the last entry in the `eventMapping` map.

### 3.3 Android build config

**`<rnSdkDir>/android/build.gradle`**
Copy `sdk/cards/android/build.gradle` exactly, then change:

- `moengageNativeBOMVersion` → `<android_bom_version>`
- `namespace` → `"<rnPackage>"`
- `api("com.moengage:cards")` → `api("com.moengage:<featureSdkArtifact>")` *(ask user if unknown)*
- `implementation("com.moengage:plugin-base-cards")` →
  `implementation("com.moengage:<androidModuleName>")`

### 3.4 Constants.kt

→ See `examples/android/Constants.kt`
Generate at: `<rnSdkDir>/android/src/main/java/<rnPackage>/Constants.kt`

Constants must include:

- One string constant per method name (camelCase), matching the contract file names exactly
- One string constant per event name (for nativeToHybrid events)

### 3.5 BridgeHandler.kt

→ See `examples/android/BridgeHandler.kt`
Generate at: `<rnSdkDir>/android/src/main/java/<rnPackage>/<rnBridgeName>Handler.kt`

Rules:

- **Fire-and-forget methods**: call
  `PluginHelper(<featureNameCamel>Helper).methodName(context, payload)` — no return
- **Promise methods, listener-based**: create a listener, call the helper, resolve/reject from the
  callback
- **Promise methods, `MoETask`-based**: call `pluginHelper.methodName(context, payload)` and chain
  `.onSuccess { result -> promise.resolve(...) }.onFailure { failure -> promise.reject(failure.reason.toString(), failure.message) }`
  directly on the returned task — don't wrap it in another listener
- **Building the resolved/rejected payload**: if plugin-base exposes a `xxxToJson`/`xxxResultToJson`
  mapper for the result type (check `Utils.kt` in the target plugin-base module first), call that
  instead of hand-rolling the response JSON with `JSONObject`/`buildJsonObject` in the bridge —
  same rule as fire-and-forget/event payloads, now applying to promise responses too. If the
  mapper needs to echo back `accountMeta` from the request, prefer a plugin-base overload that
  takes the raw request `payload: String` and parses it internally over re-parsing it yourself in
  the bridge — avoids a second JSON-parse step and keeps the bridge free of `org.json`/kotlinx
  parsing calls entirely for that method.
- **Event methods**: not in BridgeHandler — handled by EventEmitterImpl + PayloadGenerator
- Each method is annotated `@ReactMethod` (old arch); new arch goes in NewArchBridge
- Log all entry points using the module logger

### 3.6 EventEmitterImpl.kt *(only if event methods exist)*

→ See `examples/android/EventEmitterImpl.kt`
Generate at: `<rnSdkDir>/android/src/main/java/<rnPackage>/EventEmitterImpl.kt`

Implements the plugin-base `EventEmitter` interface. The `emit(event)` method switches on
`event.eventType` and calls the React-Native `sendEvent` for each known event type.

### 3.7 PayloadGenerator.kt *(only if event methods exist)*

→ See `examples/android/PayloadGenerator.kt`
Generate at: `<rnSdkDir>/android/src/main/java/<rnPackage>/PayloadGenerator.kt`

Contains one `toWritableMap()` extension function per nativeToHybrid event type. Converts the
plugin-base event's payload into a `WritableMap` for React-Native emission.

### 3.8 Package.kt

→ See `examples/android/Package.kt`
Generate at: `<rnSdkDir>/android/src/main/java/<rnPackage>/MoEngage<featureNameCamel>Package.kt`

### 3.9 Arch bridges

**New arch** → `examples/android/NewArchBridge.kt`
Path: `<rnSdkDir>/android/src/newarch/java/<rnPackage>/MoEngage<featureNameCamel>Bridge.kt`

**Old arch** → `examples/android/OldArchBridge.kt`
Path: `<rnSdkDir>/android/src/oldarch/java/<rnPackage>/MoEngage<featureNameCamel>Bridge.kt`

Rules:

- New arch: implements the TurboModule spec; delegates every method to the BridgeHandler
- Old arch: extends `ReactContextBaseJavaModule`; delegates every method to the BridgeHandler
- Both arches expose exactly the same public method signatures

### 3.10 Commit

```bash
git add <rnSdkDir>/android/
git commit -m "<ticketId>: Add React-Native Android bridge for <featureName>"
```

---

## Phase 4 — Create Pull Request

```bash
git push -u origin feature/<ticketId>-<contractSuffix>
gh pr create \
  --title "<ticketId>: Add React-Native Android bridge for <featureName>" \
  --base development \
  --body "$(cat <<'EOF'
## Summary
- Adds Android Kotlin bridge (`<rnSdkDir>/android/`) for the <featureName> feature
- BridgeHandler delegates to `<androidModuleName>` plugin-base helper
- New-arch (TurboModule) + old-arch (ReactMethod) bridges implemented
- Android BOM version: <android_bom_version>

## Related PRs
- android-plugin-base: <android_plugin_base_pr_url>

## Contract
Branch: `<contract_branch>` in mobile-sdk-contracts

## Methods
| Method | Type |
|---|---|
<table rows from method table>

🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```

---

## Phase 5 — Report & Hand-off

Print:

1. PR URL
2. Full method table (name, type, Android delegate)
3. All `// TODO` items left for manual verification
4. List of all files created or modified

**SampleApp wiring is out of scope for this skill** unless explicitly asked for. If the user does
ask for it, add manual-test entries by mirroring an existing sibling entry exactly: one button per
new method in the relevant screen's data array (e.g. `SampleApp/PushNotification.js`,
`SampleApp/HomeScreen.js` — same `{id, title, action}` shape as neighboring entries; a
Promise-returning method's action is `async` and shows the result, e.g. via `Alert.alert`), and one
`ReactMoE.setEventListener(...)` registration per new event in `SampleApp/App.js` alongside the
existing ones.

Then **ask the user**:
> "Android bridge for `<featureName>` is done (PR: <pr_url>).
> Would you like to also run the TypeScript layer now (`react-native-ts-implementation`)?
> It needs the same contract branch, BOM version, and both PR URLs."

If the user says yes, remind them to invoke:

```
/react-native-ts-implementation
  ticket_id: <ticketId>
  feature_description: <feature_description>
  contract_branch: <contract_branch>
  android_bom_version: <android_bom_version>
  plugin_base_pr_url: <plugin_base_pr_url>
  android_bridge_pr_url: <this PR URL>
```

---

## Codebase Reference Files

Read these before generating the corresponding output — copy copyright headers, logging
conventions, and structural patterns exactly.

| What                         | Codebase path                                                                                                                                                                                                                                  |
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| BridgeHandler reference      | `React-Native/sdk/cards/android/src/main/java/.../MoEngageCardsBridgeHandler.kt`                                                                                                                                                               |
| EventEmitterImpl reference   | `React-Native/sdk/cards/android/src/main/java/.../EventEmitterImpl.kt`                                                                                                                                                                         |
| PayloadGenerator reference   | `React-Native/sdk/cards/android/src/main/java/.../PayloadGenerator.kt`                                                                                                                                                                         |
| Package reference            | `React-Native/sdk/cards/android/src/main/java/.../MoEngageCardsPackage.kt`                                                                                                                                                                     |
| New-arch bridge reference    | `React-Native/sdk/cards/android/src/newarch/.../MoEngageCardsBridge.kt`                                                                                                                                                                        |
| Old-arch bridge reference    | `React-Native/sdk/cards/android/src/oldarch/.../MoEngageCardsBridge.kt`                                                                                                                                                                        |
| build.gradle reference       | `React-Native/sdk/cards/android/build.gradle`                                                                                                                                                                                                  |
| plugin-base module reference | `../android-plugin-base/<androidModuleName>/` (output of plugin-base-feature-implementation)                                                                                                                                                   |
| plugin-base JSON mappers     | `../android-plugin-base/<androidModuleName>/src/main/java/.../Utils.kt` (or module-specific `Utils.kt`/`PayloadMapper.kt`) — check for an existing `xxxToJson`/`xxxResultToJson` for the method's result type before writing one in the bridge |

---

## Error Handling Rules

- `contract_branch` not found in `../mobile-sdk-contracts` → stop and tell the user
- `contractDir` not found in `json/hybridToNative/` → list available dirs and ask
- `rnSdkDir` already has `android/src/main/` → read existing files, add only missing methods
- SDK artifact name or plugin-base helper class name unknown → add `// TODO: verify` and continue
- Push fails → report error and local branch name so the user can push manually

Files in this skill

  • SKILL.md18.2 KB
  • examples/BridgeHandler.kt2.5 KB
  • examples/Constants.kt258 B
  • examples/EventEmitterImpl.kt1.8 KB
  • examples/NewArchBridge.kt898 B
  • examples/OldArchBridge.kt1016 B
  • examples/Package.kt1.1 KB
  • examples/PayloadGenerator.kt824 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…