Home
Softono

JPush Expo

Open source MIT TypeScript
22
Stars
5
Forks
1
Issues
1
Watchers
2 months
Last Commit

 About JPush Expo

为 expo 集成 Jpush 的 expo-config 插件

Platforms

Web Self-hosted

Languages

TypeScript

Need Help Installing JPush Expo?

We provide expert installation service for this software. Our team will install, configure, and secure JPush Expo on your server. plans start at just $30.

JPush Expo

View on GitHub

MX-JPush-Expo

一个面向 Expo prebuild / Dev Client 的 JPush Config Plugin。
自动注入 iOS / Android 原生配置,完整支持主流 Android 厂商通道,保障推送送达率。

npm version CI status license Expo SDK 55 / 56 Node.js >=20.19.4

[!IMPORTANT] JPush 不支持 Expo Go。本项目面向 expo prebuild 后的原生工程,适用于 Dev Client 或原生构建流程。

[!TIP] 本项目持续参考并吸收以下资料:

目录

为什么使用它

mx-jpush-expo 把 Expo 项目接入 JPush 时最容易反复手改的原生步骤,收敛成一次 expo prebuild

  • 自动写入 iOS Info.plist 的 JPush 配置和后台模式
  • 自动写入 iOS .entitlementsaps-environment,保障 APNs 推送授权
  • 自动注入 iOS AppDelegate.swift 的 JPush 初始化与回调代码
  • 自动复用或创建 Swift Bridging Header
  • 自动修改 Android AndroidManifest.xmlbuild.gradlesettings.gradle
  • 支持华为、FCM、小米、OPPO、VIVO、魅族、荣耀、蔚来等厂商通道注入
  • 敏感参数支持环境变量注入,避免密钥明文提交到代码仓库

适合这些场景:

  • 你使用 Expo,但需要 JPush 和厂商通道能力
  • 你希望把原生改动交给 Config Plugin 管理,而不是手改生成代码
  • 你需要在 CI / 团队协作里稳定复现 prebuild 输出
  • 你需要严格的密钥安全管理,避免敏感信息泄露

支持矩阵

项目 版本
Expo SDK 55 / 56
仓库开发基线 Expo SDK 56
React Native 0.83.6(SDK 55)/ 0.85.2(SDK 56)
Node.js >= 20.19.4
jpush-react-native 3.1.9
jcore-react-native 2.3.0

快速开始

1. 安装依赖

npm install mx-jpush-expo
npm install [email protected] jcore-react-native@^2.3.0

或使用 pnpm

pnpm add mx-jpush-expo
pnpm add [email protected] jcore-react-native@^2.3.0

2. 配置插件

推荐使用 app.config.ts,并把 Android 的敏感值交给环境变量或 gradle.properties

3. 生成原生工程

npx expo prebuild

只刷新 Android:

npx expo prebuild -p android

如果你修改了 app.config.ts / app.json 中的插件参数,需要重新执行一次 prebuild,让最新配置重新落到原生工程。已经生成过原生目录时,建议按平台增量刷新;如果怀疑宿主工程里有历史残留,再使用 --clean 重新生成。

npx expo prebuild -p android --clean
npx expo prebuild -p ios --clean

推荐配置

import type { ConfigContext, ExpoConfig } from 'expo/config';
import 'dotenv/config';

export default ({ config }: ConfigContext): ExpoConfig => {
  const isProduction = process.env.EXPO_PUBLIC_ENV === 'production';

  return {
    ...config,
    plugins: [
      ...(config.plugins || []),
      [
        'mx-jpush-expo',
        {
          appKey: process.env.JPUSH_APP_KEY ?? '',
          channel: process.env.JPUSH_CHANNEL ?? 'developer-default',
          packageName:
            process.env.JPUSH_PKGNAME ?? config.android?.package ?? '',
          apsForProduction: isProduction,
          vendorChannels: {
            huawei: { enabled: true },
            fcm: { enabled: true },
            xiaomi: {
              appId: process.env.JPUSH_XIAOMI_APP_ID,
              appKey: process.env.JPUSH_XIAOMI_APP_KEY,
            },
            oppo: {
              appId: process.env.JPUSH_OPPO_APP_ID,
              appKey: process.env.JPUSH_OPPO_APP_KEY,
              appSecret: process.env.JPUSH_OPPO_APP_SECRET,
            },
            vivo: {
              appId: process.env.JPUSH_VIVO_APP_ID,
              appKey: process.env.JPUSH_VIVO_APP_KEY,
            },
            meizu: {
              appId: process.env.JPUSH_MEIZU_APP_ID,
              appKey: process.env.JPUSH_MEIZU_APP_KEY,
            },
            honor: {
              appId: process.env.JPUSH_HONOR_APP_ID,
            },
            nio: {
              appId: process.env.JPUSH_NIO_APP_ID,
            },
          },
        },
      ],
    ],
  };
};

配置要点

  • appKeychannelpackageName 仍然是插件必填项
  • iOS 初始化参数会写入 Info.plist,不再直接拼进 AppDelegate.swift
  • Android manifestPlaceholders 优先读取环境变量或 gradle.properties,缺失时会回退到插件配置里的 appKey / channel / packageName
  • 如果宿主已经定义了 manifestPlaceholders,插件会通过 manifestPlaceholders += [...] 追加 JPush 字段 非 JPush 的宿主键会被保留;如果宿主和插件都声明了同名的 JPUSH_* 键,后追加的 JPush 默认值会生效
  • Android app/build.gradle 注入不再依赖 versionName 所在行;缺少 versionName 或使用 Gradle 变量时也能稳定 prebuild
  • vendorChannels 决定要注入哪些厂商 SDK 与占位符;若声明某个厂商通道,就必须提供该厂商要求的必填字段
  • 厂商密钥仍然建议交给环境变量,避免把敏感信息直接提交到仓库

环境变量与厂商通道

Android 端的 manifestPlaceholders 读取优先级如下:

  1. System.getenv("...")
  2. project.findProperty("...")
  3. 插件收到的配置值:JPUSH_PKGNAMEJPUSH_APPKEYJPUSH_CHANNEL
  4. 空字符串,其余未提供默认值的字段

可用环境变量

变量名 说明
JPUSH_APP_KEY JPush AppKey
JPUSH_CHANNEL JPush Channel
JPUSH_PKGNAME Android 包名
JPUSH_XIAOMI_APP_ID / JPUSH_XIAOMI_APP_KEY 小米通道
JPUSH_OPPO_APP_ID / JPUSH_OPPO_APP_KEY / JPUSH_OPPO_APP_SECRET OPPO 通道
JPUSH_VIVO_APP_ID / JPUSH_VIVO_APP_KEY VIVO 通道
JPUSH_MEIZU_APP_ID / JPUSH_MEIZU_APP_KEY 魅族通道
JPUSH_HONOR_APP_ID 荣耀通道
JPUSH_NIO_APP_ID 蔚来通道

示例 .env

JPUSH_APP_KEY=your-jpush-app-key
JPUSH_CHANNEL=developer-default
JPUSH_PKGNAME=com.your.app
JPUSH_XIAOMI_APP_ID=your-xiaomi-app-id
JPUSH_XIAOMI_APP_KEY=your-xiaomi-app-key

厂商通道额外要求

厂商 额外文件 签名要求 说明
华为 agconnect-services.json 需要 需配置 SHA256 指纹
FCM google-services.json 不需要 需在 Firebase 控制台申请
荣耀 需要 需配置 SHA256 指纹
蔚来 需要 需配置应用签名
小米 不需要 仅需 AppId / AppKey
OPPO 不需要 仅需 AppId / AppKey / AppSecret
VIVO 不需要 仅需 AppId / AppKey
魅族 不需要 仅需 AppId / AppKey

官方参数说明见:极光推送 Android 厂商通道参数获取

配置说明

iOS 配置

  • appKey:JPush 后台创建应用后获得的 AppKey
  • channel:渠道标识,默认 developer-default
  • apsForProduction:是否使用生产环境 APNs,默认 false(开发环境)。插件会据此在 .entitlements 里写入 aps-environment: development / production。若该键已被 app.jsonios.entitlements、磁盘上的 .entitlements 文件或其他插件(如 expo-notifications)预先设置,则插件不会覆盖,以用户的显式配置为准

Android 配置

  • packageName:Android 应用包名,用于 manifestPlaceholders
  • 厂商通道通过 vendorChannels 对象配置,每个厂商独立开关

插件会修改哪些原生文件

平台 文件 作用
iOS ios/<app>/Info.plist 写入 JPUSH_* 配置并合并 UIBackgroundModes
iOS ios/<app>/<app>.entitlements 写入 aps-environment(已有值时不覆盖)
iOS ios/<app>/AppDelegate.swift 注入 JPush 初始化、APNs 回调和代理扩展
iOS ios/<app>/<target>-Bridging-Header.h 复用或创建桥接头文件,保证 import 幂等
Android android/app/src/main/AndroidManifest.xml 写入 JPUSH_APPKEY / JPUSH_CHANNEL meta-data
Android android/app/build.gradle 注入依赖、manifestPlaceholdersabiFilters、厂商插件
Android android/build.gradle 注入厂商 Maven 仓库与 classpath
Android android/settings.gradle 注册 jpush-react-native / jcore-react-native 模块
Android android/gradle.properties 写入华为 AGC 兼容性开关

如何验证生成结果

重新执行 expo prebuild 后,建议检查:

iOS

  • Info.plist 中存在:
    • JPUSH_APPKEY
    • JPUSH_CHANNEL
    • JPUSH_APS_FOR_PRODUCTION
  • UIBackgroundModes 会保留宿主已有值,并补齐:
    • fetch
    • remote-notification
  • <app>.entitlements 中存在 aps-environment,值为 developmentproduction
    • 若已在 app.jsonios.entitlements、磁盘文件或其他插件中设置过,则以那里的值为准
  • AppDelegate.swift 中存在 JPUSHService.setup
  • AppDelegate.swift 中的调试日志与 JPUSHService.setDebugMode() 会被 #if DEBUG 包裹,release 构建不再无条件打印
  • 如果项目使用 Swift,插件会优先复用已有 SWIFT_OBJC_BRIDGING_HEADER;未配置时会自动创建 <target>-Bridging-Header.h

Android

android/app/build.gradle 中的 JPush 占位符应保持"运行时读取",而不是写死明文:

manifestPlaceholders += [
    JPUSH_PKGNAME: System.getenv("JPUSH_PKGNAME") ?: (project.findProperty("JPUSH_PKGNAME") ?: "com.your.app"),
    JPUSH_APPKEY: System.getenv("JPUSH_APP_KEY") ?: (project.findProperty("JPUSH_APP_KEY") ?: "your-jpush-app-key"),
    JPUSH_CHANNEL: System.getenv("JPUSH_CHANNEL") ?: (project.findProperty("JPUSH_CHANNEL") ?: "developer-default")
]

也就是说:

  • 宿主原本的 manifestPlaceholders = [...] 配置会被保留
  • 对于宿主和插件都声明的 JPUSH_* 键,后追加的 JPush 值会在合并结果中生效
  • 对最终消费方,插件配置本身已经足够让 prebuild 产出可用的 Android 默认值
  • 对 CI / EAS / 多环境发布,依然推荐通过环境变量或 gradle.properties 在构建时覆盖这些值
  • 厂商通道密钥如果需要按环境切换,也应沿用同样的覆盖策略

常见问题

是否支持 Expo Go?

不支持。JPush 需要原生工程和原生依赖,必须通过 expo prebuild 进入 Dev Client 或原生构建流程。

为什么 iOS 仍然要求在插件配置里填写 appKey / channel

因为参数校验和 Info.plist 注入都发生在 Expo 配置阶段。它们不会再被直接拼进 AppDelegate.swift,但仍然需要在配置阶段提供。

Android 遇到 Gradle 插件版本错误怎么办?

如果你遇到类似 com.android.tools.build:gradle is no set in the build.gradle file 的错误,需要检查业务项目自己的 android/build.gradle 与 Expo 版本是否匹配。这不是本插件主动引入的行为变更。

直接改 node_modules/mx-jpush-expo 可以吗?

不建议。重装依赖后会丢失,正式方式建议使用 pnpm patch mx-jpush-expo 或维护自己的 fork。

插件发布后,消费方实际会拿到什么?

npm 包入口是根目录的 app.plugin.js,它会加载发布产物 plugin/build。这意味着:

  • 仓库开发者在发布新版本前,需要先执行 npm run build
  • package.jsonfiles 已包含 app.plugin.jsplugin/build,消费方不会直接运行 plugin/src 中的 TypeScript 源码
  • 如果你维护的是 fork 或 patch 包,发布前请先确认编译产物已同步更新,否则消费项目仍会执行旧逻辑

开发与测试

npm run build
npm run test -- --runInBand
npm run lint

测试覆盖重点

  • iOS Info.plist 合并与 Bridging Header 创建 / 幂等
  • iOS AppDelegate.swift 注入与幂等
  • Android Manifest、Gradle、Settings 和 gradle.properties 原生输出
  • fixture-based 回归测试,确保 compileModsAsync 输出稳定

更多开发细节见 DEVELOPMENT.md

项目结构

mx-jpush-expo/
├── app.plugin.js
├── plugin/
│   ├── src/
│   │   ├── index.ts
│   │   ├── types.ts
│   │   ├── ios/
│   │   │   ├── infoPlist.ts
│   │   │   ├── entitlements.ts
│   │   │   ├── appDelegate.ts
│   │   │   └── bridgingHeader.ts
│   │   ├── android/
│   │   │   ├── androidManifest.ts
│   │   │   ├── appBuildGradle.ts
│   │   │   ├── projectBuildGradle.ts
│   │   │   ├── settingsGradle.ts
│   │   │   └── gradleProperties.ts
│   │   └── utils/
│   │       ├── codeValidator.ts
│   │       ├── generateCode.ts
│   │       └── sourceCode.ts
│   ├── __tests__/
│   │   ├── fixtures/
│   │   ├── iosFixture.ts
│   │   ├── androidFixture.ts
│   │   └── *.test.ts
│   └── build/
├── .github/workflows/ci.yml
├── CHANGELOG.md
├── DEVELOPMENT.md
└── README.md

插件内部说明见 plugin/README.md

最近更新

完整更新历史请查看 CHANGELOG.md

  • 支持 Expo 56(React Native 0.85.2 / React 19.2),仓库开发基线升级到 Expo SDK 56,Node.js 要求提升到 >= 20.19.4
  • iOS AppDelegate.swift 注入兼容 Swift 访问级别导入(SDK 56 默认 internal import Expo
  • iOS prebuild 现在自动写入 .entitlementsaps-environment,解决推送通知无法收到的问题;若已由 app.json、磁盘文件或其他插件配置,则保留已有值
  • iOS UIBackgroundModes 改为合并写入,不再覆盖宿主已有后台模式
  • Swift Bridging Header 支持优先复用、缺失自动创建,并保持幂等
  • 补齐 iOS / Android fixture-based 原生回归测试
  • Android manifestPlaceholders 改为在宿主现有配置后追加,不再依赖 versionName 文本锚点
  • iOS AppDelegate.swift 中的 JPush 调试日志只在 DEBUG 构建启用
  • 加入 ESLint 与 CI 质量闭环
  • 对齐 Expo SDK 55 的版本声明与仓库开发基线
  • Android 敏感参数支持环境变量 / gradle.properties 读取,不再明文写入构建脚本
  • iOS 初始化参数改为从 Info.plist 读取,不再直接注入 AppDelegate.swift

致谢与许可

感谢以下资料与实现思路的启发:

本项目使用 MIT License