更新记录

1.0.0(2026-08-21)

  • 初版发布:一套面向 uni-app / uni-app x 的跨端 NFC 能力插件(UTS API 插件)。
  • 对外 API 契约统一语义化命名:类型 NfcError / TagSnapshot / NfcResult / NdefRecordInput / DispatchDiagnostics / ScannerRuntime / IosWorkbench / TagListener;函数如 isNfcSupported / startTagScan / readMifareClassicSector / transceiveIsoDep / writeNdefRecords / startSession 等。
  • Android 原生层:NfcBridge(桥接)、NfcKernel(NFC 引擎)、NfcTransitActivity(前台分发中转)。
  • iOS 原生层:NfcBridgeIOS(CoreNFC)。
  • HarmonyOS 原生层:NfcBridge(@ohos.nfc.tag / controller)。
  • 微信小程序(Android):基于 wx.getNFCAdapter() 实现标签扫描/监听与 NDEF 文本写入,读写类接口提供 *Async 异步变体;iOS 微信返回 9010007。
  • 三端均提供同步函数与 *Async 异步变体。

平台兼容性

uni-app(5.13)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ - - √ √ √ √ √
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
√ - - - - - - - - - - -

uni-app x(5.13)

Chrome Safari Android iOS 鸿蒙 微信小程序
√ √ √ √ √ √

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

lime-nfc

面向 uni-app / uni-app x 的跨端 NFC 能力插件(以函数式 UTS API 为主,附一个演示组件便于快速预览)。提供标签发现、NDEF 读写、Mifare Classic/Ultralight 读写、IsoDep/NfcV 透传等能力,覆盖 Android、iOS、HarmonyOS、微信小程序(Android)。

安装

插件市场导入,在页面引入,自定义基座。

自定义基座说明:

  • 本插件使用 UTS 原生能力,必须在自定义基座中运行,标准基座无法使用。
  • CLI 项目特别注意:请检查根目录 package.json,确保所有 @dcloudio/* 相关包的版本号一致,且与当前 HBuilderX 版本对齐。版本不一致会导致自定义基座编译失败或运行异常。

代码演示

// TS 环境:导入所需的选项/数据类型,并用 `as` 标注选项对象以获得类型校验
import type {
  ActionOptions,
  WatchOptions,
  TextWriteOptions,
  TagSnapshot
} from '@/uni_modules/lime-nfc'
import {
  isNfcSupported,
  isNfcEnabled,
  openSystemNfcSettings,
  startTagScan,
  setTagListener,
  writeTextToTag
} from '@/uni_modules/lime-nfc'

// 建议放在页面 onLoad / 按钮回调等函数内执行,避免顶层直接 return
function runNfcDemo() {
  if (!isNfcSupported()) {
    console.log('设备不支持 NFC')
    return
  }

  if (!isNfcEnabled()) {
    openSystemNfcSettings({
      fail: (err) => console.log(err.errMsg)
    } as ActionOptions)
  }

  // 方式一:回调监听
  startTagScan({
    success: (tag: TagSnapshot) => {
      console.log('发现标签', tag.serialHex, tag.textContent)
    },
    fail: (err) => {
      console.log('监听失败', err.errCode, err.errMsg)
    }
  } as WatchOptions)

  // 方式二:设置全局监听
  setTagListener((tag: TagSnapshot) => {
    console.log('标签到达', tag)
  })

  // 写入文本
  writeTextToTag({
    content: 'hello lime-nfc',
    success: () => console.log('写入成功'),
    fail: (err) => console.log('写入失败', err.errMsg)
  } as TextWriteOptions)
}

TS 与非 TS 的差异:在 TypeScript / uni-app x 中,选项对象建议用对应的选项类型(ActionOptions / WatchOptions / TextWriteOptions 等)做 as 标注;在非 TS 环境(普通 JS / 纯 UTS)中,无需 import type 这些类型,也不必写 as,直接传对象即可。

演示组件:导入后也可直接使用内置演示组件查看效果(代码位于 uni_modules/lime-nfc/components/lime-nfc):

<lime-nfc />

能力索引(意图 → 函数)

不确定用哪个函数时,先在这里按「你想做什么」定位。带「异步版本」的函数在微信小程序等异步平台必须用其 *Async 形态。

你想做什么 推荐函数 异步版本 备注
判断设备是否支持 NFC isNfcSupported — 返回 false 时其余接口返回 9010001
判断 NFC 是否已开启 isNfcEnabled — 关闭时读写失败 9010002(iOS 上该路径不可达,见下文)
引导用户打开 NFC 设置 openSystemNfcSettings — 失败 9010006
开始监听标签(回调式) startTagScan — 发现即回调 TagSnapshot
设置全局常驻监听 setTagListener — 传 null 解除
停止标签监听 stopTagScan — 彻底停止并释放
恢复挂起的监听 resumeTagScan — 沿用上次回调
主动读取最近一次标签 getLastTag — clearAfterRead 防重复
清空标签缓冲 clearTagBuffer — 批处理后调用
获取上次触碰时间 getLastTapTime — 毫秒时间戳
写入 NDEF 文本 writeTextToTag — 最常见写入
写入多条 NDEF 记录 writeNdefRecords writeNdefRecordsAsync 文本/URI 混合
写 Mifare Classic 块 writeMifareClassicBlock writeMifareClassicBlockAsync 需密钥认证
写 Mifare Ultralight 页 writeMifareUltralightPage writeMifareUltralightPageAsync 每页 4 字节
读 Mifare Classic 扇区 readMifareClassicSector readMifareClassicSectorAsync 整扇区
读 Mifare Classic 块 readMifareClassicBlock readMifareClassicBlockAsync 单块 16 字节
读 Mifare Ultralight 页 readMifareUltralightPages readMifareUltralightPagesAsync 连续多页
发送 APDU 透传(IsoDep) transceiveIsoDep transceiveIsoDepAsync 自定义/金融卡底层
发送指令透传(NfcV) transceiveNfcV transceiveNfcVAsync ISO 15693
配置扫描调度策略 configureScanner — 防抖/去重/租约
批量取出标签 getTagBatch — 配合 clearAfterRead
探测待处理 Intent probeTagIntent — Android 冷启动补处理
暂停/恢复前台扫描 pauseForegroundScan / resumeForegroundScan — 临时挂起
排查调度问题 getDispatchDiagnostics — 为何没收到标签
查看运行时状态 getScannerRuntime — 含已生效策略
查看 iOS 工作台 getIosWorkbench — 仅 iOS 有意义
开启/结束会话 startSession / stopSession — iOS 需显式开启

错误码

错误码 含义
9010001 当前设备不支持 NFC 功能
9010002 当前设备的 NFC 开关未开启
9010003 启动标签监听失败
9010004 写入标签失败
9010005 读取/透传失败(标签不支持、密钥认证失败或连接失败)
9010006 无法打开系统 NFC 设置页
9010007 当前平台暂未提供此能力
9010008 没有可用的活动标签
9010009 传入参数不合法

平台支持与权限

平台 状态
Android 完整实现(原生 NFC 引擎)
iOS 完整实现(CoreNFC)
HarmonyOS 完整实现(@ohos.nfc.tag / controller)
微信小程序 Android 微信可用(wx.getNFCAdapter 标签读写监听);iOS 微信返回 9010007
Web 兜底实现(返回 9010007 当前平台暂未提供此能力)

iOS 上 Mifare Classic 相关接口按平台能力返回占位错误,与系统 CoreNFC 限制一致。微信小程序读写底层为异步回调模型,读写为 *Async 异步接口,同步读写接口返回“请改用 *Async”提示。

  • Android:宿主工程 manifest.json 需声明 android.permission.NFC,插件已内置前台分发 Activity 与 tech 列表(res/xml/nfc_tech_routes.xml)。
  • iOS:需在 Info.plist 配置 NFC 读取权限描述与 Near Field Communication Tag Reading 能力(插件已内置示例),并部署到 iOS 13+。
  • HarmonyOS:模块已声明 ohos.permission.NFC_TAG 权限。
  • 微信小程序:使用 wx.getNFCAdapter(),需在微信开发者工具中开启 NFC 接口权限;仅 Android 微信支持标签读写。

API 参考

下面以函数为列表逐一说明:每个函数写明用途、参数(含 options 的实际字段)与返回。

设备与状态

isNfcSupported(): boolean

判断当前设备是否支持 NFC。返回 false 时其余接口会返回 9010001,建议在调用任何读写/监听前先调用。

isNfcEnabled(): boolean

判断 NFC 是否已开启。返回 false 时扫描/写入会失败并返回 9010002,应先引导用户开启。

iOS 上 isNfcEnabled 与 isNfcSupported 等价:CoreNFC 没有单独查询「系统 NFC 开关是否打开」的公开 API(仅 NFReaderSession.readingAvailable 合并了硬件 / 权限 / 限制),二者取同一来源。详见平台差异速记。

openSystemNfcSettings(options: ActionOptions): void

跳转系统 NFC 设置页,引导用户开启 NFC。失败时 fail 携带 9010006。

标签扫描与监听

startTagScan(options: WatchOptions): void

开始标签监听(回调式)。发现标签即触发 success(tag: TagSnapshot),同时 complete 也会触发。内部先校验 isNfcSupported(不支持 → 9010001)与 isNfcEnabled(未开启 → 9010002),启动失败 → 9010003。

在 iOS 上由于 isNfcEnabled 与 isNfcSupported 等价,9010002 路径实际不可达;装置缺失或受限会走 9010001,其余连接 / 权限问题由 beginWatcher 失败转为 9010003。

stopTagScan(options: ActionOptions): void

彻底停止标签监听并释放前台分发。

resumeTagScan(): void

恢复此前被挂起的监听,沿用上一次 startTagScan 注册的回调。

setTagListener(listener: TagListener): void

设置全局常驻监听。传入回调函数后,任意标签到达都会回调;传入 null 解除监听。

getLastTag(clearAfterRead: boolean): TagSnapshot | null

主动取出最近一次发现的标签快照;clearAfterRead 为 true 时取走即清空,避免重复处理。无可用标签返回 null。

clearTagBuffer(): void

清空标签缓冲队列,通常在批量处理(getTagBatch)后调用。

getLastTapTime(): number

返回上次触碰标签的毫秒时间戳。

标签写入

writeTextToTag(options: TextWriteOptions): void

最简单的文本写入:将 options.content 作为 NDEF 文本写入标签。失败 fail 携带 9010004。

writeNdefRecords(records: NdefRecordInput[]): NfcResult

写入多条 NDEF 记录(文本 / URI 混合等)。返回 NfcResult,成功 payload 含 { recordCount };记录为空 → 9010009,写入失败 → 9010004。

writeMifareClassicBlock(block: number, keySlot: string, keyHex: string, dataHex: string): NfcResult

写入 Mifare Classic 指定块(16 字节)。keySlot 为 'A' 或 'B'(密钥槽),keyHex 为 6 字节(12 位十六进制)密钥,dataHex 为 16 字节(32 位十六进制)数据。密钥非 12 位 → 9010009,认证失败 → 9010005。

writeMifareUltralightPage(page: number, dataHex: string): NfcResult

写入 Mifare Ultralight 指定页(每页 4 字节)。dataHex 为 4 字节(8 位十六进制)数据。

标签读取与透传

readMifareClassicSector(sector: number, keySlot: string, keyHex: string): NfcResult

读取 Mifare Classic 整个扇区。成功 payload 含 { sector, blocks: string[] }(每块 16 字节十六进制)。无标签 → 9010008,不支持/认证失败 → 9010005。

微信小程序端当前固定读取 4 块/扇区(标准 1K 卡即整扇区;4K 卡的大扇区含 16 块时可能读不全);iOS 该接口返回 9010007 占位,仅 Android / HarmonyOS 原生实现按实际扇区大小完整读取。

readMifareClassicBlock(block: number, keySlot: string, keyHex: string): NfcResult

读取 Mifare Classic 单块(16 字节)。成功 payload 含 { block, hex }。

readMifareUltralightPages(startPage: number, pageCount: number): NfcResult

读取 Mifare Ultralight 连续多页(每次以 4 页为单位读取)。成功 payload 含 { startPage, packets: string[] }。

transceiveIsoDep(apduHex: string): NfcResult

向 IsoDep 标签透传 APDU 指令(金融卡 / 自定义协议底层)。apduHex 为十六进制指令。成功 payload 含 { hex }(响应)。

transceiveNfcV(commandHex: string): NfcResult

向 NfcV(ISO 15693)标签透传指令。成功 payload 含 { hex }。

异步变体(Async)

微信小程序等异步平台必须使用以下 *Async 版本;同步版本在微信小程序会直接提示「请改用 *Async」。它们返回 Promise<NfcResult>,可直接 await。

  • readMifareClassicSectorAsync(sector, keySlot, keyHex): Promise<NfcResult>
  • readMifareClassicBlockAsync(block, keySlot, keyHex): Promise<NfcResult>
  • readMifareUltralightPagesAsync(startPage, pageCount): Promise<NfcResult>
  • transceiveIsoDepAsync(apduHex): Promise<NfcResult>
  • transceiveNfcVAsync(commandHex): Promise<NfcResult>
  • writeNdefRecordsAsync(records): Promise<NfcResult>
  • writeMifareClassicBlockAsync(block, keySlot, keyHex, dataHex): Promise<NfcResult>
  • writeMifareUltralightPageAsync(page, dataHex): Promise<NfcResult>

参数与返回结构与对应的同步版本一致。

调度治理

configureScanner(options: ScanPolicy | null): void

配置扫描调度策略(防抖、去重、前台通道等)。传入 null 为安全空操作:不改变任何策略、直接返回(各端实现均为 no-op)。ScanPolicy 字段:

字段 类型 含义
frontScanLane 'reader' \| 'dispatch' 前台扫描通道
omitNdefSurvey boolean 跳过 NDEF 探测
echoShieldMs number 回声屏蔽时长(毫秒)
sessionLeaseMs number 会话租约时长
inboxCap number 收件箱容量上限
repeatWindowMs number 重复去重窗口
hotTagHoldMs number 热标签保持时长
pendingLimit number 待处理上限

getTagBatch(clearAfterRead: boolean, maxCount: number): TagSnapshot[]

批量取出当前缓冲的标签(最多 maxCount 条),clearAfterRead 为 true 时取走即清空。配合 configureScanner 的去重与 clearTagBuffer 使用。

probeTagIntent(): boolean

探测并补偿处理待处理的 Android 系统 NFC Intent(常用于冷启动后补处理),返回是否有待处理 Intent。

resumeForegroundScan(): boolean

恢复前台扫描,返回是否成功恢复。

pauseForegroundScan(): void

临时挂起前台扫描(不释放监听),可用 resumeForegroundScan 恢复。

诊断与运行时

getDispatchDiagnostics(): DispatchDiagnostics | null

获取调度诊断信息,用于排查「为何没收到标签」:lastIngressAt / lastIngressSource / lastIngressSerial / lastIngressAction / cachedCount / foregroundArmed。

getScannerRuntime(): ScannerRuntime | null

获取扫描运行时状态,含已生效策略 appliedPolicy、最近一次触碰信息、bridgeLane 等。

getIosWorkbench(): IosWorkbench | null

获取 iOS 工作台信息(仅 iOS 有意义):系统版本、阅读器可达性、会话/观察者/监听器绑定状态、当前驻留标签、队列深度与已配置的去重/保持窗口等。

会话

iOS 上 NFC 需显式开启会话(弹出系统扫描 sheet),其他平台可忽略。

startSession(): void

开启 NFC 会话(iOS 弹窗)。

stopSession(): void

结束 NFC 会话。

数据类型(数据模型)

所有类型均可在 TS 环境下通过 import type 引入:

import type {
  NfcError, TagSnapshot, NfcResult, NdefRecordInput,
  DispatchDiagnostics, ScannerRuntime, IosWorkbench,
  ActionOptions, WatchOptions, TextWriteOptions, ScanPolicy, TagListener
} from '@/uni_modules/lime-nfc'

通用回调语义:操作类函数的 options 均含 success / fail / complete 三个回调字段。fail 携带 NfcError({ errCode, errSubject, errMsg }),complete 无论成败都会触发。

类型 含义 / 关键字段
NfcError 失败回调携带的错误:{ errCode, errSubject, errMsg }。errCode 见错误码;errSubject 为出错子系统(如 dispatch / ndef / classic);errMsg 为可读描述。
NdefRecordInput 构造 NDEF 记录的输入:{ kind: string, value: string }。kind 为记录类型(text / uri 等),value 为对应内容。
TagSnapshot 标签快照:serialHex(UID 十六进制)、techNames: string[]、textContent: string \| null、linkContent: string \| null、ndefLines: string[]、actionName: string、detectedAt: number、extraBag: UTSJSONObject \| null。
NfcResult 读写/透传类函数的返回值:{ ok, code, message, payload }。code 为 0 表示成功,非 0 为错误码;payload 为业务数据(见各函数说明)。
DispatchDiagnostics 调度诊断:lastIngressAt / lastIngressSource / lastIngressSerial / lastIngressAction / cachedCount / foregroundArmed。
ScannerRuntime 运行时状态:appliedPolicy(已生效策略)、最近一次触碰信息、bridgeLane 等。
IosWorkbench iOS 工作台:系统版本、阅读器可达性、会话/观察者/监听器状态、驻留标签、队列深度与去重/保持窗口等。
ActionOptions { success?, fail?, complete? },用于无业务返回值的操作。
WatchOptions 同 ActionOptions,但 success 携带 TagSnapshot。
TextWriteOptions { content: string, success?, fail?, complete? },文本写入专用。
ScanPolicy configureScanner() 入参,null 表示不修改。字段见调度治理。
TagListener (snapshot: TagSnapshot) => void,setTagListener 的监听器类型。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。