更新记录

1.4.9(2026-06-08)

  1. Android 端

    • 移除未使用参数,消除编译警告
    • stopListenNFC 增加保护逻辑,防止中断正在进行的读写操作
  2. iOS 端

    • 添加完整的 Swift 类型声明,提高类型安全性
    • 统一错误对象创建方式
  3. HarmonyOS 端

    • openNFCSettings 三级降级方案: a. ohos.settings.action.NFC_SETTINGS b. ohos.settings.action.SETTINGS c. ohos.settings.action.WIRELESS_SETTINGS
    • 兼容所有鸿蒙设备厂商
  4. 接口定义

    • NFCError.errCode 改为必填,保证错误码一致

1.4.8(2026-06-01)

harmony 平台警告消除以及兼容性问题修复

1.4.7(2026-03-03)

  • 修复ios端大小端问题
查看更多

平台兼容性

uni-app(4.87)

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

uni-app x(4.87)

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

ry-nfc

UniApp / UniApp X 通用 NFC 读写插件,支持 Android NDEF 协议和 iOS ISO14443 + ISO15693 双协议。

平台支持

平台 最低版本 uni-app uni-app x
Android API 21+
iOS 13.0+

安装

ry-nfc 目录复制到项目 uni_modules 目录下。

配置

Android

manifest.json 中添加 NFC 权限:

{
  "app-plus": {
    "distribute": {
      "android": {
        "permissions": [
          "<uses-permission android:name=\"android.permission.NFC\"/>",
          "<uses-feature android:name=\"android.hardware.nfc\" android:required=\"true\"/>"
        ]
      }
    }
  }
}

iOS

  1. 在 HBuilderX 中勾选 NFC 模块
  2. Apple Developer 后台开启 NFC Tag Reading 能力
  3. Info.plist 添加:
<key>NFCReaderUsageDescription</key>
<string>需要NFC权限用于读写NFC标签</string>

使用示例

uni-app 模式 (.vue)

<template>
    <view class="container">
        <view class="header">
            <text class="title">NFC 读写测试</text>
            <text class="status" :class="{ active: status.isReady }">
                {{ status.isReady ? '已就绪' : '未初始化' }}
            </text>
        </view>

        <!-- 状态 -->
        <view class="card">
            <text class="card-title">设备状态</text>
            <view class="info-row">
                <text>NFC支持: {{ status.isSupported ? '是' : '否' }}</text>
            </view>
            <view class="info-row">
                <text>NFC开启: {{ status.isEnabled ? '是' : '否' }}</text>
            </view>
            <view class="info-row">
                <text>监听中: {{ status.isListening ? '是' : '否' }}</text>
            </view>
        </view>

        <!-- 基础操作 -->
        <view class="card">
            <text class="card-title">基础操作</text>
            <view class="btn-row">
                <button class="btn primary" @click="doInit" :disabled="status.isReady">初始化</button>
                <button class="btn" @click="doRefresh">刷新状态</button>
            </view>
        </view>

        <!-- 读取 -->
        <view class="card">
            <text class="card-title">读取标签</text>
            <view class="btn-row">
                <button class="btn primary" @click="doRead" :disabled="!status.isReady || loading">
                    {{ loading && op === 'read' ? '扫描中...' : '读取' }}
                </button>
                <button class="btn" @click="toggleListen" :disabled="!status.isReady">
                    {{ status.isListening ? '停止监听' : '开始监听' }}
                </button>
            </view>
        </view>

        <!-- 写入 -->
        <view class="card">
            <text class="card-title">写入标签</text>
            <view class="input-row">
                <input v-model="textInput" class="input" placeholder="输入文本" />
                <button class="btn primary small" @click="doWriteText" :disabled="!status.isReady || !textInput">写入</button>
            </view>
            <view class="input-row">
                <input v-model="uriInput" class="input" placeholder="输入URI" />
                <button class="btn primary small" @click="doWriteUri" :disabled="!status.isReady || !uriInput">写入</button>
            </view>
        </view>

        <!-- 结果 -->
        <view class="card" v-if="result">
            <text class="card-title">读取结果</text>
            <view class="info-row" style="word-break: break-all;">{{ JSON.stringify(result) }}</view>
        </view>

        <!-- 底部 -->
        <view class="footer">
            <button class="btn" @click="doCancel" :disabled="!loading">取消</button>
            <button class="btn" @click="doClose">关闭</button>
        </view>

    </view>
</template>

<script>
    // uni-app 模式使用 JS/TS
    import {
        initNFC,
        getNFCStatus,
        readNDEF,
        writeText,
        writeUri,
        startListenNFC,
        stopListenNFC,
        closeNFC,
        cancelOperation
    } from '@/uni_modules/ry-nfc';

    export default {
        data() {
            return {
                status: {
                    isReady: false,
                    isListening: false,
                    isSupported: false,
                    isEnabled: false
                },
                loading: false,
                op: '',
                textInput: '',
                uriInput: 'https://',
                result: null
            };
        },

        onLoad() {
            try {
                this.doRefresh();
            } catch (e) {
                console.log('onLoad doRefresh error:', e);
            }
        },

        onUnload() {
            closeNFC();
        },

        methods: {
            doRefresh() {
                try {
                    this.status = getNFCStatus();
                } catch (e) {
                    console.log('getNFCStatus error:', e);
                    this.status = {
                        isReady: false,
                        isListening: false,
                        isSupported: false,
                        isEnabled: false
                    };
                }
            },

            async doInit() {
                if (!this.status.isSupported) {
                    uni.showToast({ title: '不支持NFC' });
                    return;
                }
                try {
                    await initNFC();
                    uni.showToast({ title: '初始化成功' });
                    this.doRefresh();
                } catch (e) {
                    console.log('initNFC error:', JSON.stringify(e));
                    let msg = '初始化失败';
                    if (e && typeof e === 'object') {
                        msg = e.errMsg || e.message || JSON.stringify(e);
                    } else if (typeof e === 'string') {
                        msg = e;
                    }
                    console.log('初始化失败===>',msg);
                    // uni.showToast({ title: msg, icon: 'none' });
                }
            },

            async doRead() {
                if (!this.status.isSupported) {
                    uni.showToast({ title: '不支持NFC' });
                    return;
                }
                this.loading = true;
                this.op = 'read';
                try {
                    uni.showLoading({ title: '请靠近标签' });
                    const res = await readNDEF();
                    console.log('readNDEF====>', JSON.stringify(res));
                    uni.hideLoading();
                    this.result = res;
                } catch (e) {
                    uni.hideLoading();
                    console.log('readNDEF error:', JSON.stringify(e));
                    let msg = e && e.errMsg ? e.errMsg : '读取失败';
                    uni.showToast({ title: msg, icon: 'none' });
                } finally {
                    this.loading = false;
                    this.op = '';
                }
            },

            toggleListen() {
                if (!this.status.isSupported) {
                    uni.showToast({ title: '不支持NFC' });
                    return;
                }
                if (this.status.isListening) {
                    stopListenNFC();
                } else {
                    startListenNFC((res) => {
                        if (res.errMsg) {
                            console.error(res.errMsg);
                            return;
                        }
                        this.result = res;
                    });
                }
                this.doRefresh();
            },

            async doWriteText() {
                if (!this.status.isSupported) {
                    uni.showToast({ title: '不支持NFC' });
                    return;
                }
                this.loading = true;
                this.op = 'writeText';
                try {
                    uni.showLoading({ title: '请靠近标签' });
                    await writeText({ text: this.textInput });
                    uni.hideLoading();
                    uni.showToast({ title: '写入成功' });
                } catch (e) {
                    uni.hideLoading();
                    console.log('writeText error:', JSON.stringify(e));
                    let msg = e && e.errMsg ? e.errMsg : '写入失败';
                    uni.showToast({ title: msg, icon: 'none' });
                } finally {
                    this.loading = false;
                    this.op = '';
                }
            },

            async doWriteUri() {
                if (!this.status.isSupported) {
                    uni.showToast({ title: '不支持NFC' });
                    return;
                }
                this.loading = true;
                this.op = 'writeUri';
                try {
                    uni.showLoading({ title: '请靠近标签' });
                    await writeUri({ uri: this.uriInput });
                    uni.hideLoading();
                    uni.showToast({ title: '写入成功' });
                } catch (e) {
                    uni.hideLoading();
                    console.log('writeUri error:', JSON.stringify(e));
                    let msg = e && e.errMsg ? e.errMsg : '写入失败';
                    uni.showToast({ title: msg, icon: 'none' });
                } finally {
                    this.loading = false;
                    this.op = '';
                }
            },

            doCancel() {
                cancelOperation();
                this.loading = false;
                this.op = '';
                uni.hideLoading();
            },

            doClose() {
                if (!this.status.isSupported) {
                    uni.showToast({ title: '不支持NFC' });
                    return;
                }
                closeNFC();
                this.doRefresh();
            }
        }
    };
</script>

<style>
    .container {
        padding: 20rpx;
        background-color: #f5f5f5;
    }

    .header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 20rpx;
    }

    .title {
        font-size: 36rpx;
        font-weight: bold;
    }

    .status {
        padding: 8rpx 16rpx;
        border-radius: 20rpx;
        background-color: #ccc;
        color: #fff;
        font-size: 24rpx;
    }

    .status.active {
        background-color: #4caf50;
    }

    .card {
        background-color: #fff;
        border-radius: 12rpx;
        padding: 20rpx;
        margin-bottom: 20rpx;
    }

    .card-title {
        font-size: 28rpx;
        font-weight: bold;
        margin-bottom: 16rpx;
    }

    .info-row {
        padding: 8rpx 0;
    }

    .content {
        color: #2196f3;
        font-weight: bold;
    }

    .raw {
        font-size: 22rpx;
        color: #999;
        word-break: break-all;
    }

    .btn-row {
        display: flex;
        gap: 16rpx;
    }

    .input-row {
        display: flex;
        gap: 16rpx;
        margin-bottom: 16rpx;
    }

    .btn {
        flex: 1;
        height: 80rpx;
        border-radius: 8rpx;
        background-color: #e0e0e0;
        font-size: 28rpx;
    }

    .btn.primary {
        background-color: #2196f3;
        color: #fff;
    }

    .btn.small {
        flex: none;
        width: 120rpx;
    }

    .input {
        flex: 1;
        height: 80rpx;
        padding: 0 16rpx;
        border: 1rpx solid #ddd;
        border-radius: 8rpx;
    }

    .footer {
        display: flex;
        gap: 16rpx;
        padding: 20rpx 0;
    }
</style>
<script>
import {
  initNFC,
  readNDEF,
  writeText,
  closeNFC,
  openNFCSettings
} from '@/uni_modules/ry-nfc';

export default {
  async onLoad() {
    try {
      await initNFC();
      console.log('NFC 初始化成功');
    } catch (e) {
      console.error(e.errMsg);
    }
  },

  onUnload() {
    closeNFC();
  },

  methods: {
    async read() {
      try {
        const res = await readNDEF();
        console.log('类型:', res.type);
        console.log('内容:', res.content);
      } catch (e) {
        console.error(e.errMsg);
      }
    }
  }
};
</script>

uni-app x 模式 (.uvue)

<script lang="uts">
import {
  initNFC,
  readNDEF,
  writeText,
  closeNFC,
  NFCReadResult,
  NFCError,
  openNFCSettings,
} from '@/uni_modules/ry-nfc';

export default {
  async onLoad() {
    try {
      await initNFC();
      console.log('NFC 初始化成功');
    } catch (e) {
      const err = e as NFCError;
      console.error(err.errMsg);
    }
  },

  onUnload() {
    closeNFC();
  },

  methods: {
    async read() {
      try {
        const res = await readNDEF();
        console.log('类型:', res.type);
        console.log('内容:', res.content);
      } catch (e) {
        const err = e as NFCError;
        console.error(err.errMsg);
      }
    }
  }
};
</script>

API 文档

initNFC()

初始化 NFC 模块。

initNFC(): Promise<NFCResult>

type NFCResult = {
  code: number   // 0=成功
  msg: string
}

getNFCStatus()

获取 NFC 状态。

getNFCStatus(): NFCStatus

type NFCStatus = {
  isReady: boolean      // 已初始化
  isListening: boolean  // 监听中
  isSupported: boolean  // 设备支持
  isEnabled: boolean    // 已开启
}

readNDEF()

读取 NFC 标签。

readNDEF(): Promise<NFCReadResult>

type NFCReadResult = {
  type: string     // text | uri | unknown
  content: string  // 内容
  raw: string      // 原始数据(十六进制)
  tagId: string    // 标签ID
}

writeText(options)

写入文本。

writeText(options: WriteTextOptions): Promise<NFCResult>

type WriteTextOptions = {
  text: string       // 文本内容
  language?: string  // 语言代码,默认 "en"
}

writeUri(options)

写入 URI。

writeUri(options: WriteUriOptions): Promise<NFCResult>

type WriteUriOptions = {
  uri: string  // http/https/tel/mailto
}

startListenNFC(callback)

开始监听。

startListenNFC(callback: NFCListenCallback): void

type NFCListenCallback = (result: NFCReadResult | NFCError) => void

stopListenNFC()

停止监听。

stopListenNFC(): void

closeNFC()

关闭 NFC 模块。

closeNFC(): void

cancelOperation()

取消当前操作。

cancelOperation(): void

错误处理

type NFCError = {
  errCode?: number
  errMsg: string
}
错误信息 说明
设备不支持NFC 无NFC硬件
请开启NFC功能 NFC未开启
请先调用 initNFC 初始化 未初始化
标签不支持NDEF协议 非NDEF标签
标签为只读状态 无法写入
会话失效 iOS超时

目录结构

ry-nfc/
├── index.uts                       # 入口 + 类型声明
├── interface.uts                   # 内部类型
├── package.json                    # 配置
├── README.md                       # 文档
└── utssdk/
    ├── app-android/
    │   ├── index.uts              # Android 实现
    │   └── config.json            # Android 配置
    └── app-ios/
        ├── index.uts              # iOS 实现
        └── config.json            # iOS 配置
    └── app-harmony/
        ├── index.uts              # HarmonyOS 实现
        └── config.json            # HarmonyOS 配置

注意事项

  1. 必须在真机上测试
  2. 标签距离建议 1-2cm
  3. 页面销毁时调用 closeNFC()
  4. iOS 会话有超时限制(约60秒)
  5. Android 仅支持 NDEF 协议标签

版本历史

  • 1.3.0: 同时支持 uni-app 和 uni-app x
  • 1.2.0: 重构,符合 UTS 插件规范
  • 1.1.0: 添加类型定义
  • 1.0.0: 初始版本

隐私、权限声明

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

NFC权限

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

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

暂无用户评论。