1. 为什么一个同名 iOS 库会出现在 OpenHarmony 工程里
1.1 RNOH 里的三方库到底是怎么“适配”的
先说一个很多从 React Native 迁移到 OpenHarmony 的开发者都会踩的误区:以为npm install装完一个 RN 三方库,构建出 hap 包后就能直接用。实际上,RN 三方库的生态基本是「JS + Android 原生 + iOS 原生」三件套,Android 和 iOS 端各自有原生模块实现。到了 OpenHarmony,这套架构完全不同:没有 Android 的 Gradle 依赖,没有 iOS 的 CocoaPods,也没有 Android 的.so和 iOS 的 framework。RNOH(React Native for OpenHarmony)能提供给我们的,是一个 JS 引擎(Hermes 或 QJS) + 自定义 Bridge + ArkTS 运行时环境。三方库要跑起来,必须补上一个 OHOS 专用的原生实现层。
RNOH 的适配方式大致分四类:
- 纯 JS 库。比如字符串处理、状态管理这类不涉及系统能力的,安装后直接跑,基本不用改。
- 已经有人做了 OHOS 适配的库。社区里很多库被移植过,包名通常带
@react-native-oh-tpl/前缀,这类你直接换包名安装即可。 - 有 iOS/Android 原生实现,但 OHOS 侧没有适配的库。这是最需要花精力的一类,也是本文要解决的核心问题。
- 重度依赖第三方 SDK 的库。比如地图、支付,这需要厂商提供 OHOS SDK,基本不能指望 RN 社区替你搞定。
react-native-push-notification-ios就是典型的第三类:有完整的 JS 接口和 iOS 原生实现,但在 OHOS 上没有现成适配包。名字虽然带ios,它的 JS API 其实长期以来被很多跨平台项目当作“通用通知接口”在使用,业务代码里到处是onRegister、onNotification、requestPermissions。因此,在迁移到 OHOS 时,最省成本的做法不是重写业务侧,而是保留 JS 层接口,在 ArkTS 侧实现一套等价的原生桥接。
1.2 push-notification-ios 这类库的价值边界
很多人在立项阶段会问:既然 OHOS 有自己的通知 API,为什么不直接写 ArkTS 通知代码,非要绕一圈去兼容一个 iOS 库?
这取决于项目从哪里来。如果是从零开发一个只服务于 OHOS 的应用,当然直接上手@ohos.notificationManager是最干净的方案。但对于已有 RN 项目、需要多端同步迭代的团队来说,业务代码里可能已经有几百处调用了createPushNotification()的实例方法,把这些调用全部改造成 OHOS 原生方式,是一件极其容易引发回归、且测试成本不低的事情。
所以,我个人的判断标准是:业务层与系统能力的边界是否清晰。如果业务层只是发通知、收点击响应、拿设备标识,那么通过适配层保持 API 语义不变,是性价比最高的方案。如果业务层需要用到通知栏大图、自定义布局、内联回复这类高度依赖系统 UI 的能力,那适配成本会飙升,直接重写业务层反而更快。
这篇文章接下来会先拆解这个库的内部逻辑,再给出一条“最小集成路径”,最后重点讲桥接层怎么用 ArkTS 实现,以及我在 RK3568 / RK3588 开发板、Turborepo Monorepo、6.0 编译环境下踩过的坑。
2. 先把 react-native-push-notification-ios 拆开看
2.1 JS 接口层:它定义了通知交互的“标准语言”
react-native-push-notification-ios的源码结构其实不复杂。入口文件对外暴露了一个createPushNotification工厂函数,返回一个实例。这个实例上挂着一组方法,业务代码中通常会这样调用:
import { createPushNotification } from 'react-native-push-notification-ios'; const pushNotification = createPushNotification(); pushNotification.onRegister((token) => { console.log('device token:', token); }); pushNotification.onNotification((notification) => { console.log('notification payload:', notification); }); pushNotification.requestPermissions();这组 JS 方法背后,实际上是在调用一个叫NativePushNotificationIOS的原生模块。原生模块由 iOS 端提供,JS 层通过NativeModules.NativePushNotificationIOS拿到句柄,然后逐个调用方法。
理解这一点很关键,因为集成到 OHOS 时,我们根本不需要动 JS 层,只需要保证 OHOS 侧也存在一个名为NativePushNotificationIOS的 TurboModule,并且实现了 JS 层调用的那几个方法即可。这就是“兼容”的本质。
2.2 iOS 原生层:底层依赖的 UNUserNotificationCenter
iOS 端的原生实现,在历史上通常依托RCTPushNotificationManager或经过重构的自定义 Manager。它具体做了这几件事:
register:调用UNUserNotificationCenter请求授权,拿到 device token 后通过onRegister回调给 JS。presentLocalNotification:通过UNNotificationRequest构建本地通知,加入通知中心。getInitialNotification:在 App 冷启动时,读取启动参数中携带的远程通知 userInfo。- 点击事件响应:通过 AppDelegate 的
didReceiveNotificationResponse方法,把用户点击的通知对象回调给 JS。 cancelAllLocalNotifications、cancelLocalNotifications:清理通知。
iOS 的通知模型是“JSON dict 为中心”的。一个通知对象长这样:
{ "alertTitle": "标题", "alertBody": "正文内容", "userInfo": { "orderId": "123456", "type": "new_order" }, "fireDate": 1700000000 }iOS 端拿到这个对象后,把alertTitle、alertBody显示在通知栏,把userInfo原样存起来,等用户点击时再带回来。这个模型简单粗暴,好处是通用性极强,坏处是很多东西要靠约定。
2.3 通知模型差异:iOS userInfo 和 OHOS NotificationRequest 的映射
到了 OpenHarmony,情况就不一样了。OHOS 的通知 API 走的是结构化NotificationRequest路线,一个本地通知的发布大致长这样(不同 API 版本字段名略有差异,以 SDK 声明为准):
import notificationManager from '@ohos.notificationManager'; const request: notificationManager.NotificationRequest = { id: 1, content: { notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, normal: { title: '通知标题', text: '通知正文', additionalText: '附加信息' } }, notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION }; notificationManager.publish(request, (err) => { if (err) { console.error('publish failed:', err.code, err.message); return; } console.info('publish success'); });所以,桥接层要做的核心工作不是简单的 API 翻译,而是设计一套消息格式转换协议。我的做法是这样的映射:
| JS 层(push-notification-ios) | iOS 侧 | OHOS 桥接侧 |
|---|---|---|
requestPermissions | UNUserNotificationCenter 授权 | notificationManager.requestEnableNotification() |
presentLocalNotification | 构建 UNNotificationRequest 并加入中心 | 把 JS 字典转换为 NotificationRequest,再publish |
getInitialNotification | 读取冷启动 userInfo | 读取 EntryAbility 冷启动 want 中的参数 |
| 通知点击回调 | AppDelegate 的 didReceive 方法 | WantAgent 触发,拿到参数后通过 DeviceEvent 回传 JS |
cancelAllLocalNotifications | 删除全部通知请求 | notificationManager.cancelAll() |
这个表格是桥接层的“需求说明书”。每一行背后都有不少细节,尤其是getInitialNotification和点击回调,它们在生命周期上的坑比想象中多。
3. 动手前的准备工作:版本匹配、设备确认与工程结构
3.1 “npm install 了三方库”不等于“OHOS 能跑”
RNOH 的典型工程结构,不是把 RN 工程硬塞进 DevEco Studio 工程,而是分两层:
- 外层是标准的 React Native 工程,有
package.json、node_modules、index.js等。 - 内层是一个由脚手架(
@react-native-oh/react-native-harmony)生成的entry/ohos目录,它本质是一个 OpenHarmony 应用壳工程。
最终构建出的 hap 包,是把外层生成的 JS bundle 打包进壳工程里的。也就是说,凡是 OHOS 侧需要的原生依赖,必须出现在entry/ohos的oh-package.json5里,而不是单纯出现在外层 npm 依赖里。react-native-push-notification-ios没有 OHOS 原生实现,所以安装它之后,还需要自己在entry/ohos下创建一个 ArkTS 模块,并在模块构建配置中注册,才能让 JS 层的NativeModules.NativePushNotificationIOS找到对应的 TurboModule。
具体步骤我在下一节给出。先提醒环境层面的准备。
3.2 hdc 下的设备信息查询:版本、UDID 与 product.name
OpenHarmony 日常调试基本离不开hdc命令。它跟 ADB 很相似,但又不是完全一样。三个最常用的查询命令:
# 列出当前连接的设备 hdc list targets # 查看系统版本和完整版本名 hdc shell param get const.ohos.fullname hdc shell param get const.ohos.apiversion # 查看设备型号/开发板型号 hdc shell param get const.product.name # 查看设备 UDID(测试推送、通知 token 时经常需要) hdc shell bm get --udid这里有一个隐藏的坑:const.product.name这个参数在部分开发板的系统镜像中被定制成了产品代号,比如RK3568、rk3588_s之类。不要在你的应用逻辑里依赖这个参数作为设备标识,它不是稳定的业务 ID,只适合做硬件型号识别。真正稳定的设备标识用hdc shell bm get --udid获取的 UDID。
我曾经见过一个团队把const.product.name当成设备唯一 ID 传给后端做消息推送关联,结果换了一块 RK3588 开发板后,后端把所有消息都发到了旧设备上,排查大半天才发现是设备标识字段用错了。
3.3 RK3568 / RK3588 的差异与 Turborepo 下的注意点
如果你在 RK3568 上跑 RNOH 应用,对性能要有心理预期。它的内存和 GPU 能力相对有限,RN 的 JS bundle 在 debug 模式下加载会有明显卡顿。我的建议是:
- RK3568 上尽量用 release 包进行功能验证,不要在 debug 模式下看着卡顿就以为代码写崩了。
- 如果项目是 Turborepo 管理的 Monorepo,
node_modules往往被提升到仓库根目录。RNOH 的自动链接工具在扫描原生模块时,遇到 symlink 和 workspace 场景有时候会漏扫。遇到这种情况,可以先npx react-native-config生成一份配置文件看看依赖树,或者手动在entry/ohos中声明依赖。
另外,OpenHarmony 6.0 编译环境与 RNOH 的版本匹配要特别注意。RNOH 每个版本都只支持特定范围的 RN 版本,比如常见的 0.72.x、0.75.x。在开搞之前先查你用的 RN 版本对应的 RNOH release note,确认它能跑在目标板子的系统版本上,这个步骤能节省后面大量的排障时间。
4. 最小集成路径:把本地通知先跑起来
4.1 安装、自动链接与手动注册
先正常安装 npm 包:
npm install react-native-push-notification-ios然后用 RNOH 的自动链接工具试一下,看它能否识别出这个库的 OHOS 原生模块:
npx react-native config大概率在nativeModules里看不到它,因为包目录下没有harmony子目录。没关系,我们直接手动建桥接模块。
在entry/ohos下新建一个名为NotificationModule的 ArkTS 模块,目录结构大致如下:
entry/ohos/ ├── entry/ │ └── src/main/ │ ├── ets/ │ │ ├── entryability/ │ │ ├── pages/ │ │ └── notification/ │ │ └── NotificationModule.ets │ └── module.json5 └── oh-package.json5然后在entry/ohos的模块配置里,把这个模块注册为依赖。
注意:RNOH 0.72 之后的版本对 TurboModule 的注册方式非常敏感,模块名必须与 JS 侧
NativeModules中访问的名称完全一致,大小写都不能错。NativePushNotificationIOS里那个大写的IOS,是最容易敲错的地方。
4.2 通知权限与渠道的初始化
OHOS 上发通知,首先要判断用户是否授权。RNOH 应用一般会在入口文件Index.ets中先请求通知权限:
import notificationManager from '@ohos.notificationManager'; notificationManager.requestEnableNotification((err) => { if (err) { console.error('requestEnableNotification failed:', JSON.stringify(err)); return; } console.info('requestEnableNotification success'); });这个 API 会拉起系统通知授权弹窗,和 iOS 的UNUserNotificationCenter授权弹窗是同一位置的作用。在部分系统镜像上,如果应用已经授权过,弹窗不会重复出现,所以要注意在授权成功后做一个本地标记,免得每次启动都调一次。
通知渠道(NotificationSlot)是另一个容易漏的环节。建议在应用启动时初始化:
import notificationManager from '@ohos.notificationManager'; const SLOT_ID = 'default_slot'; const slot: notificationManager.NotificationSlot = { slotType: notificationManager.SlotType.SOCIAL_COMMUNICATION, level: notificationManager.SlotLevel.DEFAULT, desc: '默认通知渠道', authorized: true, enableVibration: true, enableLight: true, lightColor: 0xFF0000 }; notificationManager.addSlot(slot, (err) => { if (err) { console.error('addSlot failed:', err.code, err.message); return; } console.info('addSlot success'); });如果不先创建渠道,在某些 API 版本上直接publish会报“slot not exist”之类的错误。这个坑在模拟器上不一定复现,但在真机开发板上非常常见。
4.3 发布一条本地通知的 ArkTS 代码
在桥接模块里,presentLocalNotification的核心逻辑是把 JS 传入的通知字典转换成 OHOS 的NotificationRequest。我在这里给出一个简化版的实现:
import notificationManager from '@ohos.notificationManager'; import { TurboModule } from '@rnoh/react-native-openharmony/ts'; export class NotificationModule extends TurboModule { presentLocalNotification(options: Record<string, Object>): void { const title = (options['alertTitle'] ?? '') as string; const body = (options['alertBody'] ?? '') as string; const userInfo = (options['userInfo'] ?? {}) as Record<string, Object>; const request: notificationManager.NotificationRequest = { id: Date.now() % 100000, content: { notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, normal: { title: title, text: body, additionalText: '' } }, notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION, deliveryTime: Date.now(), showDeliveryTime: true }; notificationManager.publish(request, (err) => { if (err) { console.error('publish failed:', JSON.stringify(err)); return; } console.info('publish success'); }); } }这里用Date.now() % 100000生成通知 ID 是一种妥协做法,因为 OHOS 通知 ID 不能重复,如果同一秒内发多条通知,会互相覆盖。实际项目中建议维护一个自增 ID 计数器。
4.4 用适配层保住 JS 业务代码的兼容性
上面的桥接模块注册好后,JS 侧有两种接法。第一种是只管自己的方法名:
import { NativeModules } from 'react-native'; NativeModules.NativePushNotificationIOS.presentLocalNotification({ alertTitle: '新订单', alertBody: '您有一笔新的订单待处理', userInfo: { orderId: '123456' } });第二种,也是我更推荐的做法:写一个薄适配层,把react-native-push-notification-ios的工厂函数输出替换成调用 OHOS 桥接模块的包装。这样业务侧可以继续使用pushNotification.onRegister(...)这类写法,后续要再切回 iOS,只需要改适配层的内部实现。
适配层的另一个重要职责是事件分发。OHOS 桥接模块中,通知点击事件是通过 RNOH 的emitDeviceEvent发到 JS 侧的。适配层需要监听这些事件,并调用业务侧注册的onNotification回调。这部分逻辑放到桥接层一起讲。
5. 桥接层工程化:把 iOS 的 API 翻译成 ArkTS 通知服务
5.1 TurboModule 骨架与事件通路
RNOH 的 TurboModule 写法,核心是一个继承自TurboModule的类,加上装饰器声明,然后重写getMethods或者使用方法装饰器。各版本的装饰器写法有差异,下面给出我在 0.72.x 系列上验证过可行的骨架:
import { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; export class NativePushNotificationIOSModule extends TurboModule { private notificationMap: Map<number, Record<string, Object>> = new Map(); private idCounter: number = 1; constructor(ctx: TurboModuleContext) { super(ctx); } requestPermissions(): void { notificationManager.requestEnableNotification((err) => { if (err) { this.ctx.logger.error('requestEnableNotification failed'); return; } // 授权成功后,可以通过事件或回调通知 JS 侧 this.ctx.rnInstance.emitDeviceEvent('remoteNotificationsRegistered', {}); }); } presentLocalNotification(options: Record<string, Object>): void { const id = this.idCounter++; this.notificationMap.set(id, options); const title = (options['alertTitle'] ?? '') as string; const body = (options['alertBody'] ?? '') as string; const request: notificationManager.NotificationRequest = { id: id, content: { notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, normal: { title: title, text: body } }, notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION }; notificationManager.publish(request); } }事件通路是所有桥接层最容易出问题的地方。JS 侧通过DeviceEventEmitter接收事件,而 OHOS 侧通过this.ctx.rnInstance.emitDeviceEvent发送。两边的事件名必须完全一致。
建议集中管理事件名常量,比如'remoteNotificationsRegistered'、'notificationOpened'、'localNotificationReceived',不要把字符串散落在代码里,否则调用方改了一个字符就会造成“事件发不出去,但又不报错”的诡异问题。
5.2 getInitialNotification 与点击事件的完整链路
getInitialNotification在 iOS 上对应冷启动时读取 remote notification 的 userInfo。在 OHOS 上,这个信息藏在 EntryAbility 的onCreate参数 want 里。
我的实现思路是:在 EntryAbility 启动时,把 cold-start want 里的参数保存到一个单例对象(或者全局变量)中,桥接模块暴露getInitialNotification()方法,JS 侧在 App 初始化阶段调用这个方法,读取保存的参数并返回。
关键代码如下(EntryAbility 侧):
import { common, Want } from '@kit.AbilityKit'; import { GlobalThisHelper } from '../common/GlobalThisHelper'; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: common.LaunchParam): void { const initialNotification = want?.parameters?.['notificationUserInfo']; if (initialNotification) { GlobalThisHelper.getInstance().setInitialNotification(initialNotification); } } }桥接模块侧:
getInitialNotification(): Object | undefined { const initial = GlobalThisHelper.getInstance().getInitialNotification(); return initial ?? undefined; }这个方案有个细节要提醒:冷启动参数只在进程被系统拉起时才存在。如果应用已经在后台,用户从通知栏点击通知,走的是onNewWant,而不是onCreate。所以你的 EntryAbility 同时要处理onNewWant场景:
onNewWant(want: Want, launchParam: common.LaunchParam): void { const notificationUserInfo = want?.parameters?.['notificationUserInfo']; if (notificationUserInfo) { this.ctx.rnInstance.emitDeviceEvent('notificationOpened', notificationUserInfo); } }判断“冷启动还是热启动”是点击通知回调的主要逻辑分歧点。我的通用做法是:冷启动时保存参数,等 JS 侧调getInitialNotification时返回;热启动时直接 emit 事件。两边互补,避免重复。
5.3 线程与生命周期:最容易踩坑的地方
桥接层里第二个高频坑是线程。notificationManager.publish的回调、WantAgent 的回调,都不保证在 JS 线程执行。如果你在回调里直接调用this.ctx.rnInstance.emitDeviceEvent,在某些 CPU 架构或高并发场景下,可能出现事件丢失或时序错乱。
我的建议是,把所有发送给 JS 的事件都封装到一个统一的emitToJs(eventName, payload)方法中,在这个方法内部对调用线程做一份串行化处理:
private emitToJs(eventName: string, payload: Object): void { this.ctx.uiTaskQueue.executeTask(() => { this.ctx.rnInstance.emitDeviceEvent(eventName, payload); }); }uiTaskQueue可以把任务调度到 UI 线程串行执行,极大降低线程切换带来的事件竞争问题。这个封装成本很低,但在真机上实测能减少大约一半的偶发回调丢失问题。
生命周期方面,onNewWant触发时,RNInstance 可能还未完成 JS 侧监听器注册。我的做法是先缓存事件,延迟 300ms 再发送;或者等 RN 加载完成标记。总之不要假设onNewWant一定发生在 JS 监听器就绪之后。
6. 构建、部署与实战排障记录
6.1 hap 构建产物与缓存清理
RNOH 工程的构建链路,一般分两步:
# 第一步:生成 JS bundle npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output ./entry/ohos/entry/src/main/resources/rawfile/bundle.harmony.js --assets-dest ./entry/ohos/entry/src/main/resources/rawfile这一步生成的bundle.harmony.js会被打包进 hap 的 rawfile 目录。第二步才是在 DevEco Studio 或命令行中用 hvigor 构建 hap。
构建产物和缓存相关,常被问到的几件事:
entry/build目录:这是 hap 构建的中间产物目录,可以整个删掉重新构建,不会有副作用。entry/ohos/.hvigor:hvigor 的缓存目录,如果出现改完 Native 代码但运行时行为没变化的诡异问题,先删它。node_modules/.cache:metro 和 babel 的缓存,JS bundle 不更新时删它。oh_modules下的.tsbuildinfo文件:ArkTS 编译增量缓存,出现类型检查报错与实际代码不符时清除。
Turborepo 场景还要额外清.turbo缓存目录。我总结一个“三连清”命令:删掉entry/build、.hvigor、.turbo,再重新构建,能解决大部分“构建成功但行为不对”的玄学问题。
6.2 hdc 部署、日志查看与常见失败点
构建出 hap 后,部署命令:
# 安装 hap hdc install -r entry-default-signed.hap # 启动应用 hdc shell aa start -a EntryAbility -b com.example.yourapp # 查看日志 hdc shell hilog | grep ReactNativeJS如果安装时报签名错误或INSTALL_PARSE_FAILED,优先检查认证签名。RNOH 项目在 DevEco 中默认会生成调试签名,但命令行直接构建可能需要额外配置签名。
日志查看方面,我建议开两个终端:一个专门过滤 JS 日志(grep ReactNativeJS),一个专门过滤 ArkTS 层日志(grep NotificationModule),这样做能快速定位问题出在桥接层还是业务层。
常见失败点还有一个:hdc install -r之后应用图标没有出现在桌面,或者点击图标启动时报module.json5中 ability 不存在。这种一般是你改了entry模块名称,但aa start命令里的-b包名没有同步修改。检查module.json5里的bundleName和EntryAbility是否一致。
6.3 一次“点击通知无回调”的完整排查
我举一个真实排查案例:本地通知能正常弹出来,但点击通知后,JS 侧的onNotification回调一直没有触发。
排查链路如下:
第一步,确认 WantAgent 是否正确挂载。点击通知能够拉起应用,说明 WantAgent 是有的。但在某些系统版本上,WantAgent 拉起应用并不代表notificationUserInfo一定传了进来。我需要先在 EntryAbility 的onNewWant里加一行 hilog 打印,确认参数是否到达。
onNewWant(want: Want, launchParam: common.LaunchParam): void { console.info(`onNewWant want: ${JSON.stringify(want)}`); }第二步,确认事件发送时机。如果onNewWant在应用冷启动后立即触发,而 JS 侧的DeviceEventEmitter监听器还没有注册完,事件就会丢失。我的处理是加一个“RN 加载完成”标记,在 RN 侧回调一个notifyReady方法后再发送挂起事件。
第三步,确认桥接模块是否被正确注册。如果 JS 侧调用NativeModules.NativePushNotificationIOS拿到的是undefined,那它根本连桥接模块都没找到,更别提事件通路。检查方式非常简单,在 JS 侧打一行日志:
console.log('NativePushNotificationIOS:', NativeModules.NativePushNotificationIOS);如果这里输出 undefined,问题大概率出在模块注册名称或构建缓存上,先清理构建产物再试。
整个排查过程下来,最终定位到两个问题叠加:一是点击事件在 JS 监听器注册完成前被发出;二是首次构建缓存导致模块注册失败。两个问题都不复杂,但叠加在一起,很容易让人误以为是通知 API 本身的问题。
最后再分享两个小技巧
一个是通知渠道的清理。在开发板上反复安装卸载 hap 时,旧的通知渠道有时候不会被卸载逻辑清掉。如果你发现发通知的行为和代码预期不符,先手动删掉旧的 slot,再重新安装,可以避免很多莫名其妙的现象。
另一个是用notificationManager.on('click')做全局点击监听时,注意在页面销毁时调用off解绑。RNOH 应用的生命周期和原生应用不完全一致,页面返回后事件监听可能仍然存在,内存泄漏不容易被察觉,但累计到一定量就会导致通知点击事件被重复处理。
就我个人体验来说,RN 三方库迁移到 OpenHarmony 这件事,真正的核心从来不是“把 npm 包装上”,而是理解这套“JS 接口约定 + 原生桥接实现”的解耦逻辑。react-native-push-notification-ios只是众多三方库中的一个缩影,把它的桥接逻辑吃透,后面再遇到react-native-image-picker、react-native-device-info这类库,思路基本是通用的。