@webority/mobile-core

中文文档
0.119.0发布于 2026/11/03SEE LICENSE IN LICENSE.txtTypeScriptESM + CJS

webbority React Native应用的平台层:HTTP客户端、认证/令牌存储、配置、日志、格式化器、验证器、网络状态、权限、存储、版本检查和无线更新。没有用户界面。

中文使用文档
文档目录7 节
  1. 导入路径
  2. 离线副本
  3. 通知按钮
  4. 构建号标头和强制更新
  5. 静默请求和日志编校
  6. 应用锁
  7. 地铁桥

@webority / mobile-core

web React Native应用的平台层:HTTP客户端、认证和令牌存储、配置、 日志,格式化程序,验证程序,网络状态,权限,存储,图像压缩和 版本检查。 没有用户界面 -组件生活在 @webority/mobile-ui.

npm install @webority/mobile-core

运行时依赖关系: axios 和 libphonenumber-js。所需的对等体有 expo, expo-application, expo-network, expo-secure-store (HTTP客户端从 它), expo-sqlite。其他本地对等体都是可选的,只能通过自己的导入来访问 下面的路径,因此包安装和运行时不需要它们。 react-native-keychain 没有 不再使用;令牌存储移至 expo-secure-store.

完整的文档位于存储库的 docs/ 目录中。

导入路径

任何需要原生包的东西都在它自己的路径后面,所以一个应用只打包它需要的东西 进口:

import { Permissions } from '@webority/mobile-core/permissions';
import { Geolocation } from '@webority/mobile-core/geolocation';
import { openDatabase } from '@webority/mobile-core/sqlite';
import { downloadFile } from '@webority/mobile-core/download';
import { shareFile } from '@webority/mobile-core/share';
import { fetchAttachment, fetchPreviewAttachment, previewKind } from '@webority/mobile-core/attachments';
import { getOrCreateInstallationId, clearTokensOnFirstLaunch } from '@webority/mobile-core/installation';
import { requestReviewAfterSuccess } from '@webority/mobile-core/review';
import { initPush, enablePush, usePush } from '@webority/mobile-core/push';
import { definePushActionTask } from '@webority/mobile-core/push/task';
import { compressImage } from '@webority/mobile-core/compressor';
import { initOtaUpdates, useOtaUpdates } from '@webority/mobile-core/updates';
import { createApiBaseQuery, createAppApi, setupApiListeners } from '@webority/mobile-core/query';

Metro解析是静态的,所以任何可以从主桶访问的东西都被绑定 每一个消费者。让他们远离这些是让他们的同伴真正可以选择的原因。

/query 为RTK查询层;它需要 @reduxjs/toolkit (2.12或更高版本)和 react-redux, 都是可选的对等体。一个 createApi 每个应用程序,构建与 createAppApi 基本查询结束 库的客户端,所以刷新,重试,脱机检查和 extractApiError 失败 形状停留在一个地方。从来没有一个 fetchBaseQuery.

// services/api/apiSlice.ts
import { publicClient, userClient } from '@webority/mobile-core';
import { createApiBaseQuery, createAppApi, buildListQuery, providesPagedList,
  type PagedDataObject } from '@webority/mobile-core/query';

export const apiSlice = createAppApi({
  reducerPath: 'api',
  baseQuery: createApiBaseQuery({ client: userClient, publicClient }),
  tagTypes: ['Project'],
  endpoints: (build) => ({
    getProjects: build.query<PagedDataObject<Project>, { page: number }>({
      query: ({ page }) => ({ url: '/api/projects', params: buildListQuery({ page, pageSize: 20 }) }),
      providesTags: providesPagedList('Project')
    })
  })
});
// store.ts: add apiSlice.reducer and apiSlice.middleware, then setupApiListeners(store.dispatch)

setupApiListeners 将应用程序映射到前台(AppState)到RTK查询的焦点, 并将本馆的网络状态分为线上和线下;RTK自己的默认值 window 从未在设备上触发的事件。有标记的呼叫 silent: true的端点 extraOptions: { silent: true }, silent 在 action.meta.baseQueryMeta,其中 吐司中间件跳过错误。

/push 为手机注册推送通知。它需要 expo-notifications,可选 同行和报告 unavailable 没有它或在世博会上。呼叫 initPush 在开始的时候 getInstallationId, register, unregister 和 onOpen; enablePush 请求许可,发送 本地令牌(Fcm 在Android上, Apns 在iOS上)和bundle id to register, 当令牌更改时重新注册; disablePush 调用 unregister 开关只推一次 它成功了(失败拒绝并留下push)。 resetPush 把电话按下,没有 服务器调用并丢弃本机令牌,用于没有会话剩余的签出。 onOpen 接收 点击通知的数据为字符串,包括启动应用的点击。 usePush 给出了一个 设置开关 status 和 enabled。库不记得这个开关:一个应用程序 保持来电推送 enablePush 每次出发。 setBadgeCount(count) 设置应用图标徽章。 dismissAllNotifications() 清除通知托盘中的每个通知;它从不抛出,并在warn处记录失败。

  • enablePush({ prompt: false }) 从不显示权限提示:已授予的电话 静默注册,并且任何其他都以其状态(undetermined 当人已不在 有人问过, denied 当拒绝时,提示仍然可以显示, blocked 当只有 设置可以打开它)。在签到时打电话,然后打电话 enablePush() 当 人到了问问题有意义的时候。
  • 每个注册设备都携带 timeZone,手机的IANA区域(Asia/Kolkata),或为空 平台不报告任何信息,因此服务器可以在用户的本地时间发送信息。
  • 每个注册设备都携带 apnsEnvironment: Development 或者 Production 在iOS上,请阅读 从构建的 aps-environment (expo-application),因此服务器将每个电话发送到 apn网关其签名接受。它在Android上为空,在iOS上无法读取时为空 模拟器);然后服务器使用它自己的设置。
  • onReceive(handler) 调用 handler({ notificationId, title, body, data }) 为每一个 通知在应用打开时到达,显示或不显示,所以应用可以刷新列表或 未读计数。它返回一个删除处理程序的函数。通知到达 背景没有达到它;它的水龙头仍然是 onOpen.

离线副本

createPersistedQueries 将所选端点的答案保存在手机上,因此打开了一个屏幕 “无连接”显示最后一次加载数据的时间。列出的端点保存到 Storage 当它们装载时; restore 一开始就放回去。

  1. 通过 extractRehydrationInfo 来 createAppApi.
  2. 添加 middleware 在api的中间件之后。
  3. 呼叫 restore(store.dispatch) 在创建存储之后。
  4. 呼叫 clear() 签到,所以下一个接电话的人永远不会看到上一个人的数据。
// services/api/offlineQueries.ts
export const offlineQueries = createPersistedQueries({
  storageKey: 'offline.queries',
  endpoints: ['getProjects']
});

// services/api/apiSlice.ts: add extractRehydrationInfo: offlineQueries.extractRehydrationInfo
// store.ts: reducer: { [apiSlice.reducerPath]: apiSlice.reducer }
//           middleware: (getDefault) => getDefault().concat(apiSlice.middleware, offlineQueries.middleware)
offlineQueries.restore(store.dispatch); // once, after the store exists
// sign-out
await offlineQueries.clear();

还原后的答案立即显示,无需取回,所以设置 refetchOnMountOrArgChange: true 在每一个 保留端点:挂载的屏幕再次询问服务器并刷新已恢复的数据。

通知按钮

push命名一个类别;这个类别的按钮是在手机上注册的。在以下网址登记 每次开始,在两个平台上使用相同的id:

import { setCategories, notifyLocal } from '@webority/mobile-core/push';

await setCategories([
  {
    id: 'approval',
    actions: [
      { id: 'approve', title: 'Approve', opensApp: false },
      { id: 'reject', title: 'Reject', destructive: true }
    ]
  },
  {
    id: 'reply',
    actions: [
      {
        id: 'reply',
        title: 'Reply',
        opensApp: false,
        textInput: { submitTitle: 'Send', placeholder: 'Write a reply' }
      }
    ]
  }
]);

opensApp 默认为true; destructive 和 authenticationRequired (首先解锁)是iOS 只有。点击按钮到达 { actionId, userText, data, notificationId }: userText 是 回复文本, data push数据作为字符串, notificationId 通知的标识符为 电话。一个普通的水龙头仍然是 onOpen; 没有处理程序的按钮点击像普通点击一样打开。

要接收每个应用状态下的按钮点击,被杀死的应用包括call definePushActionTask 在根组件注册之前,在入口文件的模块范围内。它需要 expo-task-manager,可选的对等体(npx expo install expo-task-manager),所以它以自己的方式生活 自己的路:

// index.js
import { definePushActionTask } from '@webority/mobile-core/push/task';
import { notifyLocal } from '@webority/mobile-core/push';

definePushActionTask(async ({ actionId, userText, data }) => {
  try {
    await settle(actionId, userText, data);
  } catch {
    await notifyLocal({ title: 'Could not approve', body: 'Open the app to try again.' });
  }
});

等待处理程序的promise,因此它的请求可以在后台工作结束之前完成。 onAction(handler) 从 /push 在JavaScript运行时为点击设置相同的处理程序 返回一个删除它的函数。在10秒内报告了两次回应(发射为, 侦听器和Android任务可以各自报告它运行一次。

  • Android: 控件上的按钮点击时,后台通知任务运行处理程序 按钮只出现在通知上,博览会通知绘图 例子 notifyLocal). 这是一款应用程序,它可以从纯数据推送中获取自己的通知 每个按钮的意图都要用世博会的 NotificationsService.createNotificationResponseIntent, 所以tap到达这个处理程序。
  • iOS: 一个带有 opensApp: false 在后台启动应用程序;响应 到达听众 definePushActionTask 设置。任务未在iOS上注册。

存储和网络状态保持在主桶上,但不加载自己的包。把它们连接在 启动与 setStorageImplementation 和 setNetworkStatusImplementation 如果你想要的话。

构建号标头和强制更新

两个HTTP客户端都发送 App-Build: <integer> 在每个请求上:应用程序的整个构建号 (VERSION_INTERNAL, Android版本码,iOS CFBundleVersion)。API将其与最小值进行比较 答案是426的旧建筑 client_update_required。这个数字来自 initWebority({ appBuild }) (setConfig({ appBuild })), else来自expo-application’s nativeBuildVersion。如果没有整数,报头将被删除,并且服务器永远不会拒绝 没有它的请求。 Client-Build (<appVersion>+<commit>)不变。

两个HTTP客户端也发送 Client-App: <appId> 当配置有一个app id (initWebority({ appId }))。 它是应用程序的商店标识符,因此API可以区分相同客户端类型的应用程序。

版本层只来自于 Min-App-Version 响应头。当一个答复带着它, useVersionCheck 转 isUpdateRequired 对于它下面的构建。一个426 client_update_required 不移动地板,所以没有标题的426离开地板。426永远不会 重试。

应用程序通过运行移动到该合约 webority upgrade 0.93.0。它重新命名旧的 X- 头 字符串到普通名称, ApiError.detail 来 diagnostic 和 detail.signOutReason 来 detail.code,并列出每个已删除的选项(correlationIdHeader, signOutReasonHeader), 一个人要删除的常量。

静默请求和日志编校

两个HTTP客户端都记录了一个失败的响应 Logger.error。对于一个呼叫失败的应用程序 处理自己,传递 skipErrorLog: true 在axios配置和客户端保持沉默:

await userClient.get('/status', { skipErrorLog: true });

Logger 在任何接收器看到它们之前隐藏每个消息和参数中的秘密: Bearer 令牌, jwt形状的字符串,以及名为 token, accessToken, refreshToken, password 和 authorization (任何情况下)的对象和JSON字符串。

configureLogger({ redactKeys }) 将应用程序键添加到内置屏蔽列表中,并且永远不会删除一个。 Logger.getBreadcrumbs() 返回最后50个面包屑,最老的放在第一位,已经编辑; configureLogger({ breadcrumbLimit }) 改变盖子。 initWebority 这是必需的 remoteLogger水槽,或者 null 对于没有崩溃报告的构建。这也是必需的 appId,商店id (APP_ID 在appsettings。Json,没有环境后缀),以及一个必需的 environment。它添加了环境的后缀(.development, .staging当结果与已安装的应用的id不同时,拒绝启动。

应用锁

@webority/mobile-core/appLock 当一个登录的人在锁定延迟(默认为5分钟)后返回时,锁定应用程序。 useAppLock 返回设置屏幕和锁定屏幕需要的内容:

import { useAppLock } from '@webority/mobile-core/appLock';

const { locked, enabled, biometricAvailable, availabilityKnown, setEnabled, unlock } = useAppLock({ active: isSignedIn });
  • availabilityKnown 在第一次生物识别检查结束前都是假的。在那之前 biometricAvailable 还不是答案,所以屏幕应该等待,然后显示锁不可用或禁用其开关。
  • setEnabled(next) 和 unlock(reason) 返回一个 AppLockResult: { ok: true },或者 { ok: false, reason } 有了理由 cancelled, lockout, not-enrolled, not-available 或者 failed.
  • policy 覆盖延迟和两个存储键;默认值用于任何遗漏的键。

地铁桥

@webority/mobile-core/metro 构建Metro开发工具所读取的开发桥梁 globalThis.__METRO_BRIDGE__。应用程序在下面分配它 __DEV__; 建造者没有添加自己的检查。

import { createMetroBridge } from '@webority/mobile-core/metro';

if (__DEV__) {
  globalThis.__METRO_BRIDGE__ = createMetroBridge({ store, navigationRef, signIn, signOut, isSignedIn });
}
  • storeRedux存储,通读 getState 写了一遍 dispatch.
  • navigationRef:导航容器的ref (isReady, getRootState, canGoBack, goBack, navigate)。
  • signIn(request) 解决了 { codeRequired }; signOut() 一旦会话被清除就解析; isSignedIn() 返回一个布尔值。
  • commands (可选):额外命名的命令。一个名字 signIn, signOut 或者 navigate 抛出 创建桥的时候。

这座桥暴露了 commands (signIn, signOut, navigate,以及额外费用), navigation (getState,在导航器准备好之前,该值为空 goBack,返回 { wentBack }), redux, auth (isSignedIn), push (notifyLocal 和 deviceToken,设备的推送令牌)。

版本与发布记录

@webority/mobile-core 共发布 101 个版本,首次发布于 2026/10/04;过去 12 个月发布了 101 个版本。

版本发布日期距今
0.119.0latest2026/11/03今天
0.118.02026/11/03今天
0.117.02026/11/03今天
0.116.02026/11/02今天
0.115.02026/11/02今天
0.114.02026/11/02今天

关于 @webority/mobile-core 的常见问题

@webority/mobile-core 是什么?

webbority React Native应用的平台层:HTTP客户端、认证/令牌存储、配置、日志、格式化器、验证器、网络状态、权限、存储、版本检查和无线更新。没有用户界面。

如何安装 @webority/mobile-core?

在项目目录运行 npm install @webority/mobile-core,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:expo(>=57.0.0)、react(^19.1.0)、expo-crypto(>=57.0.0)、expo-sqlite(>=57.0.0)、expo-network(>=57.0.0)、react-native(>=0.81.0)、expo-application(>=57.0.0)、expo-secure-store(>=57.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@webority/mobile-core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@webority/mobile-core 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@webority/mobile-core 可以商用吗?

@webority/mobile-core 使用 SEE LICENSE IN LICENSE.txt 许可。许可条款写在包内的独立文件中,请阅读该文件确认可否商用。以上为概括,不构成法律意见,请以许可原文为准。

@webority/mobile-core 还在维护吗?

最新版本 0.119.0 发布于 2026/11/03(今天);至今共发布 101 个版本,其中过去 12 个月发布了 101 个;首次发布于 2026/10/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。