更新记录

1.0.17(2025-12-25)

  1. 优化鸿蒙

1.0.16(2025-12-15)

  1. 优化

1.0.15(2025-12-12)

  1. 增加Android app在杀死、前台、后台运行时获取刷卡数据的逻辑
查看更多

平台兼容性

uni-app(3.6.15)

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

uni-app x(3.6.15)

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

其他

多语言 暗黑模式 宽屏模式

NFC TAG NDEF读写,支持Android、iOS、harmony

  1. 读写文本、url、external等等
  2. 刷卡打开url
  3. 刷卡打开app应用

集成步骤

  1. 下载demo示例,拷贝demo里的nativeResources、harmony-configs、AndroidManifest.xml、Info.plist到项目里面
  2. iOS创建identifers时,要在Capabilites栏目里勾选NFC Tag Reading功能(Identifiers -> xxxx.xxx.xx(点击对应包名) -> Capabilities -> NFC Tag Reading,勾选后再生成.mobileprovision)
  3. 集成插件步骤请参考https://www.cnblogs.com/wenrisheng/p/18323027
  4. demo里为了文本转码使用了插件https://ext.dcloud.net.cn/plugin?name=wrs-uts-modbuscrchex
  5. 如需定制请进入交流群私聊作者

插件变量引入


import {
    UTSNFCTag
} from "@/uni_modules/wrs-uts-nfc"

let nfcTag = new UTSNFCTag()

NFC读卡

先调用准备读卡接口,然后卡放到手机NFC感应区,感应到有卡以后按照下面处理

  • 准备读卡

let params = {}
switch (uni.getSystemInfoSync().platform) {
    case 'android': {

    }
    break;
    case 'ios': {
        params.pollingOptions = ["iso14443"] // iso14443、iso15693、iso18092、pace
        params.alertMessage = "Tag 读取中..."
    }
    break;
    case 'harmonyos': {

    }
    break;
}
nfcTag.beginReadNFC(params)
  1. Android感应到NFC卡后,会自动回调页面到onShow接口,然后调用nfcTag.readNDEF接口获取卡数据

let params = {}
nfcTag.readNDEF(params, (resp) => {
    console.log("android NFC:" + JSON.stringify(resp))
    let messages = resp.messages
    if (messages && messages.length > 0) {
        for (let i = 0; i < messages.length; i++) {
            let message = messages[i]
            let records = message.records
            if (records && records.length > 0) {
                for (let j = 0; j < records.length; j++) {
                    let record = records[j]
                    this.parseRecord(record)
                }
            }
        }

    }
})
  1. iOS感应到NFC卡后,会回调nfcTag.setCallback里的didDetectTags,然后调用连接tag接口connectTag后再调用nfcTag.readNDEF接口获取卡数据

let params = {}
params.tagIndex = this.tagIndex // 连接第几个tag, tag在数组中的索引位置
nfcTag.connectTag(params, (resp) => {
    this.showMsg("connectTag:" + JSON.stringify(resp))
    if (resp.flag) { // 连接成功
        let statusParams = {}
        statusParams.tagIndex = this.tagIndex
        nfcTag.readNDEF(statusParams, (ndefResp) => {
            nfcTag.invalidate()
            this.showMsg("readNDEF:" + JSON
                .stringify(
                    ndefResp))
            let message = ndefResp.message
            if (message) {
                let records = message.records
                if (records && records.length >
                    0) {
                    for (let i = 0; i < records
                        .length; i++) {
                        let record = records[i]
                        this.parseRecord(record)
                    }
                }
            }
        })
    } else { // tag连接失败
        nfcTag.invalidate()
        this.showToast("tag连接失败")
    }
})
  1. harmony感应到NFC卡后,会回调nfcTag.setCallback里的didDetectTags,回调结果里面带有NFC卡数据

case "didDetectTags":
{
    ............
    for (let i = 0; i < tags.length; i++) {
        let tag = tags[i]
        let type = tag.type
        if (type == "NDEF") {
            let message = tag.message
            if (message) {
                let records = message.records
                if (records && records.length > 0) {
                    for (let j = 0; j < records.length; j++) {
                        let record = records[j]
                        this.parseRecord(record)
                    }
                }
            }
        }
    }
}
  • 解析卡数据方法请参考demo里的parseRecord(record)接口

读卡数据样例子:

// ios 
{
    "opt": "didDetectTags",
    "tags": [{
        "id": [29, 219, 175, 6, 10, 16, 128], // 卡uid
        "isAvailable": false,
        "type": "miFare",
        "historicalBytes": [],
        "mifareFamily": 1
    }]
}

{
    "message": {
        "size": 33,
        "records": [{ // 卡记录数据
            "id": [],
            "type": [85],
            "tnf": 1,
            "payload": [2, 97, 105, 106, 105, 97, 110, 116, 97, 110, 46, 99, 111, 109, 63, 110, 97, 109, 101,
                61, 119, 114, 115, 38, 97, 103, 101, 61, 49
            ]
        }]
    }
}

// android:
{
    "tag": {
        "techList": ["android.nfc.tech.NfcA", "android.nfc.tech.Ndef"],
        "id": [29, -37, -81, 6, 10, 16, -128],  // 卡uid
        "ndef": {
            "type": "org.nfcforum.ndef.type2",
            "isWritable": true,
            "canMakeReadOnly": true,
            "maxSize": 137
        }
    },
    "id": [29, -37, -81, 6, 10, 16, -128],
    "messages": [{
        "byteArray": [-111, 1, 29, 85, 2, 97, 105, 106, 105, 97, 110, 116, 97, 110, 46, 99, 111, 109, 63, 110,
            97, 109, 101, 61, 119, 114, 115, 38, 97, 103, 101, 61, 49, 81, 1, 13, 85, 0, 109, 121, 97, 112,
            112, 58, 47, 47, 104, 111, 115, 116
        ],
        "records": [{  // 卡记录数据
            "tnf": 1,
            "type": [85],
            "tnfValue": "TNF_WELL_KNOWN",
            "id": [],
            "typeValue": "RTD_URI",
            "byteArray": [-47, 1, 29, 85, 2, 97, 105, 106, 105, 97, 110, 116, 97, 110, 46, 99, 111, 109,
                63, 110, 97, 109, 101, 61, 119, 114, 115, 38, 97, 103, 101, 61, 49
            ],
            "payload": [2, 97, 105, 106, 105, 97, 110, 116, 97, 110, 46, 99, 111, 109, 63, 110, 97, 109,
                101, 61, 119, 114, 115, 38, 97, 103, 101, 61, 49
            ]
        }, {
            "tnf": 1,
            "type": [85],
            "tnfValue": "TNF_WELL_KNOWN",
            "id": [],
            "typeValue": "RTD_URI",
            "byteArray": [-47, 1, 13, 85, 0, 109, 121, 97, 112, 112, 58, 47, 47, 104, 111, 115, 116],
            "payload": [0, 109, 121, 97, 112, 112, 58, 47, 47, 104, 111, 115, 116]
        }]
    }],
    "action": "android.nfc.action.NDEF_DISCOVERED"
},

// harmony
{
    "opt": "didDetectTags",
    "id": [29, 219, 175, 6, 10, 16, 128],  // 卡uid
    "technology": [1, 6],
    "tags": [{
        "technology": 1,
        "type": "NFC_A"
    }, {
        "technology": 6,
        "type": "NDEF",
        "message": {
            "records": [{  // 卡记录数据
                "tnf": 1,
                "type": [85],
                "id": [],
                "payload": [1, 98, 97, 105, 100, 117, 46, 99, 111, 109]
            }]
        },
        "isNdefWritable": true,
        "canSetReadOnly": true
    }]
}
  • NFC写卡

先调用准备写卡接口,然后卡放到手机NFC感应区,感应到有卡以后按照下面处理

  • 准备写卡

let params = {}
switch (uni.getSystemInfoSync().platform) {
    case 'android': {
        // this.androidReadNFCData()
    }
    break;
    case 'ios': {
        params.pollingOptions = ["iso14443"]
        params.alertMessage = "Tag 写入中..."
    }
    break;
    case 'harmonyos': {
        // let params = {}
        // nfcTag.readNFC(params)
    }
    break;
}
nfcTag.beginWriteNFC(params)
  1. Android感应到NFC卡后,会自动回调页面到onShow接口,然后调用nfcTag.writeNDEF接口写入数据
  2. iOS感应到NFC卡后,会回调nfcTag.setCallback里的didDetectTags,然后调用连接tag接口connectTag后再调用nfcTag.writeNDEF接口写入数据
  3. harmony感应到NFC卡后,会回调nfcTag.setCallback里的didDetectTags,在这里调用nfcTag.writeNDEF接口写入数据

let params = this.getWriteNDEFData()
params.tagIndex = this.tagIndex // 
nfcTag.writeNDEF(params, (resp) => {
    console.log("writeNDEF resp:" + JSON.stringify(resp))
    if (resp.flag) { // 数据写入成功
        this.showMsg("数据写入成功")
    } else {
        this.showMsg("数据写入失败:" + JSON.stringify(resp))
    }
    nfcTag.invalidate()
})
  • 写卡参数里record类型
  1. raw原始数据
{
    type: "raw", // 写入原始数据
    data: {
        tnf: 0x00, // 
        type: [0x54],
        id: [0x00],
        payload: [0x00]
    }
}

// tnf:
// 0x00: TNF_EMPTY 
// 0x01: TNF_WELL_KNOWN 
// 0x02: TNF_MIME_MEDIA 
// 0x03: TNF_ABSOLUTE_URI
// 0x04: TNF_EXTERNAL_TYPE
// 0x05: TNF_UNKNOWN
// 0x06: TNF_UNCHANGED

// type:
// [0x54]: RTD_TEXT "T"
// [0x55]: RTD_URI "U"
// [0x53, 0x70] RTD_SMART_POSTER "Sp"
// [0x61, 0x63] RTD_ALTERNATIVE_CARRIER "ac"
// [0x48, 0x63] RTD_HANDOVER_CARRIER "Hc"
// [0x48, 0x72] RTD_HANDOVER_REQUEST "Hr"
// [0x48, 0x73] RTD_HANDOVER_SELECT "Hs"
  1. url数据
{
    type: "url", // 写入原始数据
    data: "https://www.baidu.com"
}
  1. external数据
{
    type: "external", 
    data: {
        domain: "android.com",
        type: "pkg",
        data: [0x00]
    }
 }
  • 取消NFC卡感应

nfcTag.invalidate()

刷卡启动app业务

ios

方式1

NFC卡里写入自定义scheme,项目按照uniapp教程配置"iOS设置UrlSchemes"https://uniapp.dcloud.net.cn/tutorial/app-ios-capabilities.html,刷卡后系统会弹出横幅让用户选择打开哪个app来处理nfc

方式2

NFC卡里写入https通用链接,项目按照uniapp教程配置"iOS通用链接配置"https://uniapp.dcloud.net.cn/tutorial/app-ios-capabilities.html,刷卡后系统会弹出横幅让用户选择打开哪个app来处理nfc

android

方式1

NFC卡里写入自定义scheme,项目按照uniapp教程配置"android设置UrlSchemes"配置教程https://uniapp.dcloud.net.cn/tutorial/app-ios-capabilities.html,刷卡后系统会弹出横幅让用户选择打开哪个app来处理nfc

方式2

NFC卡里写入external类型的数据,刷卡后可以直接打开app

ndefRecords.push({
    type: "external", 
    data: {
        domain: "android.com", // 固定
        type: "pkg",
        data: strToArray("uni.app.UNID460C83") // 包名
    }
})

android如果需要获取刷NFC卡启动app时的数据(app在杀死、前台、后台运行都能获取到),需要取消AndroidManifest.xml里面注释的代码,然后删除手机上的app,重新自定义基座运行,刷卡后会有一个一闪而过的过渡页面,如果要取消这个过渡页,需要本地离线打包,可以上面"进入交流群"联系作者

harmony

方式1

NFC卡里写入自定义scheme,项目按照鸿蒙教程配置Deep Linkinghttps://developer.huawei.com/consumer/cn/doc/harmonyos-guides/deep-linking-startup,刷卡后系统会弹框让用户选择打开哪个app来处理nfc

方式2

NFC卡里写入https通用链接,项目按照鸿蒙教程配置App Linkinghttps://developer.huawei.com/consumer/cn/doc/harmonyos-guides/applinking-preparations,刷卡后直接打开app

获取刷卡启动app时的卡数据

在app启动的时候获取nfc卡数据


        onShow: function() {
    console.log('App Show')

switch (uni.getSystemInfoSync().platform) {
    case 'android': {
        // app未启动时,刷卡启动app,通过UTSNFCTag.getLaunchNFCData接口获取nfc数据
        let result = UTSNFCTag.getLaunchNFCData({})
        console.log("getLaunchNFCData result:" + JSON.stringify(result))
        let keySet = result.keySet
        if (keySet) {
            // 判断是否是刷external格式内容的卡启动的app
            if (keySet.hasOwnProperty('profile')) {
                // {"keySet":{"short_cut_class_name":"io.dcloud.PandoraEntry","profile":"UserHandle{0}","__intetn_orientation__":"2"},"action":"android.intent.action.MAIN"}
                // model.msg = "用户点击桌面图标启动app"
                // uni.$emit('updateMsg', {
                //  msg: model.msg
                // })
                console.log("用户点击桌面图标启动app")
            } else {
                // {"keySet":{"short_cut_class_name":"io.dcloud.PandoraEntry","__intetn_orientation__":"2"},"action":"android.intent.action.MAIN"}
                // model.msg = "用户刷external卡启动app"
                // uni.$emit('updateMsg', {
                //  msg: model.msg
                // })
                console.log("用户刷external卡启动app")
            }
        }

        // 下面app在杀死时刷卡启动、前台/后台运行时,获取NFC卡数据生效的话,需要取消AndroidManifest.xml里面注释的代码,然后删除手机上的app,重新自定义基座运行,刷卡后会有一个一闪而过的过渡页面,如果要取消这个过渡页,需要本地离线打包,可以联系作者***
        // 当app杀死时,通过刷卡启动app
        let messages = result.messages
        if (messages && messages.length > 0) {
            let message = messages[0]
            let records = message.records
            if (records) {
                let resultArray = parseRecordArray(records)
                model.msg = JSON.stringify(resultArray)
                console.log("UTSNFCTag.onSetCallback:" + model.msg)
                uni.$emit('updateMsg', {
                    msg: model.msg
                })
            }
        }
        // 当app在后台或前台运行时,刷卡启动app时会回调这里
        UTSNFCTag.onSetCallback((resp) => {
            // console.log("UTSNFCTag.setCallback resp:" + JSON.stringify(resp))
            let messages = resp.messages
            if (messages && messages.length > 0) {
                let message = messages[0]
                let records = message.records
                if (records) {
                    let resultArray = parseRecordArray(records)
                    model.msg = JSON.stringify(resultArray)
                    console.log("UTSNFCTag.onSetCallback:" + model.msg)
                    uni.$emit('updateMsg', {
                        msg: model.msg
                    })
                }
            }
        })

        let args = plus.runtime.arguments;
        if (args) {
            // nfc卡写入scheme时刷卡启动app,刷卡时系统会自动弹出横幅,点击横幅选择app打开
            // myapp://host?name=wrs&age=15
            // 处理args参数,如直达到某新页面等
            model.msg = JSON.stringify(args)
            uni.$emit('updateMsg', {
                msg: model.msg
            })
            console.log("runtime arguments:" + JSON.stringify(args))
        }
    }
    break;
    case 'ios': {
        // nfc卡写入scheme时刷卡启动app,刷卡时系统会自动弹出横幅,点击横幅选择app打开
        let args = plus.runtime.arguments;
        if (args) {
            // myapp://host?name=wrs&age=15
            // 处理args参数,如直达到某新页面等
            model.msg = JSON.stringify(args)
            uni.$emit('updateMsg', {
                msg: model.msg
            })
            console.log("runtime arguments:" + JSON.stringify(args))
        }
    }
    break;
    case 'harmonyos': {
        // deep link(自定义scheme)刷卡时会,刷卡时系统会自动弹框选择用哪个app打开,选择app打开
        // app link刷卡时直接启动app
        let args = plus.runtime.arguments;
        if (args) {
            // 处理args参数,如直达到某新页面等
            let argsObj = JSON.parse(args)
            if (argsObj) {
                let NdefMsg = argsObj.NdefMsg
                if (NdefMsg) {
                    let recordArray = UTSNFCTag.parseNdefMsg(NdefMsg)
                    let resultArray = parseRecordArray(recordArray)
                    model.msg = JSON.stringify(resultArray)
                    uni.$emit('updateMsg', {
                        msg: model.msg
                    })
                }
            }
        }
    }
    break;
}
}

隐私、权限声明

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

Android: <uses-permission android:name="android.permission.NFC" /> ios: "NFCReaderUsageDescription" : "NFC读卡业务需要您的授权"

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

插件不采集任何数据

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