UNPKG

mx-jpush-expo

Version:

Expo 集成极光推送(JPush)一体化解决方案,支持 iOS/Android 厂商通道

393 lines (305 loc) 15.4 kB
<div align="center"> <h1>MX-JPush-Expo</h1> <p> 一个面向 <strong>Expo prebuild / Dev Client</strong> 的 JPush Config Plugin。<br /> 自动注入 iOS / Android 原生配置,完整支持主流 Android 厂商通道,保障推送送达率。 </p> <p> <a href="https://www.npmjs.com/package/mx-jpush-expo"><img alt="npm version" src="https://img.shields.io/npm/v/mx-jpush-expo?logo=npm&label=npm"></a> <a href="https://github.com/konodioda727/JPush-Expo/actions/workflows/ci.yml"><img alt="CI status" src="https://img.shields.io/github/actions/workflow/status/konodioda727/JPush-Expo/ci.yml?branch=main&logo=githubactions&label=CI"></a> <a href="https://github.com/konodioda727/JPush-Expo/blob/main/LICENSE"><img alt="license" src="https://img.shields.io/github/license/konodioda727/JPush-Expo"></a> <img alt="Expo SDK 55+" src="https://img.shields.io/badge/Expo%20SDK-55%2B-000020?logo=expo"> <a href="https://nodejs.org/"><img alt="Node.js >=18.18.0" src="https://img.shields.io/badge/Node.js-%3E%3D18.18.0-339933?logo=nodedotjs&logoColor=white"></a> </p> </div> > [!IMPORTANT] > JPush 不支持 Expo Go。本项目面向 `expo prebuild` 后的原生工程,适用于 Dev Client 或原生构建流程。 > [!TIP] > 本项目持续参考并吸收以下资料:<br> > - [JPush 集成 Expo](https://juejin.cn/post/7423235127716659239) > - [Expo SDK 53+ 集成极光推送 iOS Swift](https://juejin.cn/post/7554288083597885467) > - [RunoMeow/jpush-expo-config-plugin](https://github.com/RunoMeow/jpush-expo-config-plugin) ## 目录 - [为什么使用它](#为什么使用它) - [支持矩阵](#支持矩阵) - [快速开始](#快速开始) - [推荐配置](#推荐配置) - [环境变量与厂商通道](#环境变量与厂商通道) - [配置说明](#配置说明) - [插件会修改哪些原生文件](#插件会修改哪些原生文件) - [如何验证生成结果](#如何验证生成结果) - [常见问题](#常见问题) - [开发与测试](#开发与测试) - [项目结构](#项目结构) - [最近更新](#最近更新) - [致谢与许可](#致谢与许可) ## 为什么使用它 `mx-jpush-expo` 把 Expo 项目接入 JPush 时最容易反复手改的原生步骤,收敛成一次 `expo prebuild`- 自动写入 iOS `Info.plist` 的 JPush 配置和后台模式 - 自动注入 iOS `AppDelegate.swift` 的 JPush 初始化与回调代码 - 自动复用或创建 Swift `Bridging Header` - 自动修改 Android `AndroidManifest.xml``build.gradle``settings.gradle` - 支持华为、FCM、小米、OPPO、VIVO、魅族、荣耀、蔚来等厂商通道注入 - 敏感参数支持环境变量注入,避免密钥明文提交到代码仓库 适合这些场景: - 你使用 Expo,但需要 JPush 和厂商通道能力 - 你希望把原生改动交给 Config Plugin 管理,而不是手改生成代码 - 你需要在 CI / 团队协作里稳定复现 `prebuild` 输出 - 你需要严格的密钥安全管理,避免敏感信息泄露 ## 支持矩阵 | 项目 | 版本 | | --- | --- | | Expo SDK | `55+` | | 仓库开发基线 | `Expo SDK 55` | | React Native | `0.83.6` | | Node.js | `>= 18.18.0` | | `jpush-react-native` | `3.1.9` | | `jcore-react-native` | `2.3.0` | ## 快速开始 ### 1. 安装依赖 ```bash npm install mx-jpush-expo npm install jpush-react-native@3.1.9 jcore-react-native@^2.3.0 ``` 或使用 `pnpm````bash pnpm add mx-jpush-expo pnpm add jpush-react-native@3.1.9 jcore-react-native@^2.3.0 ``` ### 2. 配置插件 推荐使用 `app.config.ts`,并把 Android 的敏感值交给环境变量或 `gradle.properties`### 3. 生成原生工程 ```bash npx expo prebuild ``` 只刷新 Android: ```bash npx expo prebuild -p android ``` 如果你修改了 `app.config.ts` / `app.json` 中的插件参数,需要重新执行一次 `prebuild`,让最新配置重新落到原生工程。已经生成过原生目录时,建议按平台增量刷新;如果怀疑宿主工程里有历史残留,再使用 `--clean` 重新生成。 ```bash npx expo prebuild -p android --clean npx expo prebuild -p ios --clean ``` ## 推荐配置 ```ts 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, }, }, }, ], ], }; }; ``` ### 配置要点 - `appKey``channel``packageName` 仍然是插件必填项 - 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_PKGNAME``JPUSH_APPKEY``JPUSH_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````bash 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 厂商通道参数获取](https://docs.jiguang.cn/jpush/client/Android/android_3rd_param) ## 配置说明 ### iOS 配置 - `appKey`:JPush 后台创建应用后获得的 AppKey - `channel`:渠道标识,默认 `developer-default` - `apsForProduction`:是否使用生产环境 APNs,默认 `false`(开发环境) ### Android 配置 - `packageName`:Android 应用包名,用于 `manifestPlaceholders` - 厂商通道通过 `vendorChannels` 对象配置,每个厂商独立开关 ## 插件会修改哪些原生文件 | 平台 | 文件 | 作用 | | --- | --- | --- | | iOS | `ios/<app>/Info.plist` | 写入 `JPUSH_*` 配置并合并 `UIBackgroundModes` | | 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` | 注入依赖、`manifestPlaceholders``abiFilters`、厂商插件 | | 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` - `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 占位符应保持"运行时读取",而不是写死明文: ```gradle 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.json` 的 `files` 已包含 `app.plugin.js` 与 `plugin/build`,消费方不会直接运行 `plugin/src` 中的 TypeScript 源码 - 如果你维护的是 fork 或 patch 包,发布前请先确认编译产物已同步更新,否则消费项目仍会执行旧逻辑 ## 开发与测试 ```bash 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](./DEVELOPMENT.md)。 ## 项目结构 ```text mx-jpush-expo/ ├── app.plugin.js ├── plugin/ │ ├── src/ │ │ ├── index.ts │ │ ├── types.ts │ │ ├── ios/ │ │ │ ├── infoPlist.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](./plugin/README.md)。 ## 最近更新 完整更新历史请查看 [CHANGELOG.md](./CHANGELOG.md)。 - 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` ## 致谢与许可 感谢以下资料与实现思路的启发: - [JPush 集成 Expo](https://juejin.cn/post/7423235127716659239) - [Expo SDK 53+ 集成极光推送 iOS Swift](https://juejin.cn/post/7554288083597885467) - [RunoMeow/jpush-expo-config-plugin](https://github.com/RunoMeow/jpush-expo-config-plugin) 本项目使用 [MIT License](./LICENSE)。