更新记录

1.0.7(2024-06-23)

  1. 重构iOS版本

1.0.6(2024-04-13)

  1. 重构Android数据接口

1.0.5(2024-03-28)

  1. 适配新版本UTS语法
查看更多

平台兼容性

HbuilderX/cli最低兼容版本
3.6.8

uni-app

Vue2 Vue3
?
app-vue app-nvue app-android app-ios
? ? ? ?
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
? ? ? ? ? ? ? ? ?
微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序 钉钉小程序 快手小程序 飞书小程序 京东小程序
? ? ? ? ? ? ? ? ?
快应用-华为 快应用-联盟
? ?

uni-app x

app-android app-ios
? ?
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
? ? ? ? ? ? ? ? ?

读写NFC卡,支持Tag的nded、非ndef(mifareUltralight)

  1. 读写文本(写入字节)
  2. 刷卡打开url
  3. 刷卡打开app应用
  4. 支持Tag、NDEF、mifareUltralight等

支持定制,联系QQ252797991

集成步骤

  1. 下载demo示例,拷贝demo里的nativeResources、Info.plist到项目里面
  2. iOS创建identifers时,要在Capabilites栏目里勾选NFC Tag Reading功能(Identifiers -> xxxx.xxx.xx(点击对应包名) -> Capabilities -> NFC Tag Reading,勾选后再生成.mobileprovision)
  3. Android增加nfc权限,即 "<uses-permission android:name=\"android.permission.NFC\"/>"
  4. 集成插件请参考https://www.jianshu.com/p/c1615a7880a7

插件变量引入


    import {
    getNFCFrom,
    cleanIntentNFCData,
    enableForegroundDispatch,
    disableForegroundDispatch,
    initNFC,
    readNFC,
    writeNFCNdefRecords,
    writeNFCNdefUrls,
    writeNFCNdefPackages,
    writeNFCMifareUltralight,
    UTSNFCReaderWriter,
    gotoNFCActivity,
    UTSNFCTagReaderSession,
    UTSNFCNDEFReaderSession
} from "@/uni_modules/wrs-uts-nfc"
let tagReaderSession = new UTSNFCTagReaderSession()
let ndefReaderSession = new UTSNFCNDEFReaderSession()

示例demo用到ascii/十六进制转换插件https://ext.dcloud.net.cn/plugin?id=12803

android NFC读写

  • 读取NFC卡数据(NDEF、非NDEF),解析数据参考demo

// 读取NFC数据
var params = {}
// 非必传,如果需要读取非NDEF数据,则需要传mifareUltralight参数
// params.mifareUltralight = {
//  pageOffsets: [this.pageOffset]
// }
let resp = readNFC(params)
this.showMsg(JSON.stringify(resp))
let action = resp.action
if (action) {
    if (action == "android.nfc.action.TAG_DISCOVERED") {
        let tag = resp.tag
        if (tag) {

        } else {
            this.showMsg("NFC标签是没有格式化的,或者没有分区的执行此步")
        }
    } else if (action == "android.nfc.action.ADAPTER_STATE_CHANGED") {

    } else if (action == "android.nfc.action.NDEF_DISCOVERED") {
        let ndefMessagesStr = resp.ndefMessages
        let ndefMessages = JSON.parse(ndefMessagesStr)
        console.log(ndefMessages)
        if (ndefMessages && ndefMessages.length > 0) {
            let firstNdefMessages = ndefMessages[0]
            let records = firstNdefMessages.records
            if (records && records.length > 0) {
                let firstRecord = records[0]
                let typeValue = firstRecord.typeValue
                switch (typeValue) {
                    // 文本
                    case "RTD_TEXT":
                        console.log("文本")
                        // 解析文本
                        this.parseNdefText(firstRecord)
                        break;
                        // url
                    case "RTD_URI":
                        console.log("URL")
                        // 解析url
                        this.parseUrl(firstRecord)
                        break
                    default:
                        break;
                }
            }
        }
    } else if (action == "android.nfc.action.TECH_DISCOVERED") {

    }
}
  1. android NDEF数据

{
    "ndefMessages": "[{\"byteArray\":[-47,1,18,84,2,122,104,78,70,67,45,78,101,119,84,101,120,116,45,49,50,51],\"records\":[{\"tnf\":1,\"type\":[84],\"id\":[],\"byteArray\":[-47,1,18,84,2,122,104,78,70,67,45,78,101,119,84,101,120,116,45,49,50,51],\"mimeType\":\"text/plain\",\"payload\":[2,122,104,78,70,67,45,78,101,119,84,101,120,116,45,49,50,51]}]}]",
    "tag": {
        "techList": ["android.nfc.tech.NfcA", "android.nfc.tech.MifareUltralight", "android.nfc.tech.Ndef"],
        "id": [4, -75, -23, 120, -72, 42, -127],
        "mifareUltralightPageDatas": "[{\"data\":[1,3,-96,12,52,3,22,-47,1,18,84,2,122,104,78,70],\"pageOffset\":4}]",
        "ndef": {
            "type": "org.nfcforum.ndef.type2",
            "isWritable": true,
            "canMakeReadOnly": true,
            "maxSize": 137
        }
    },
    "id": [4, -75, -23, 120, -72, 42, -127],
    "action": "android.nfc.action.NDEF_DISCOVERED"
}
  1. android Tag(非NDEF)数据

{
    "tag": {
        "techList": ["android.nfc.tech.NfcA", "android.nfc.tech.MifareUltralight", "android.nfc.tech.NdefFormatable"],
        "id": [4, -15, -120, -110, 94, 17, -112],
        "mifareUltralightPageDatas": "[{\"data\":[-79,-79,-66,-87,-55,-49,-70,-93,-71,-29,-42,-35,-52,-20,-67,-14],\"pageOffset\":4}]"
    },
    "id": [4, -15, -120, -110, 94, 17, -112],
    "action": "android.nfc.action.TAG_DISCOVERED"
}
  • NFC写入ndef文本数据(写入字节)

// ASCIIUtils工具类有插件(https://ext.dcloud.net.cn/plugin?id=12803)提供,请前往市场插件页面(https://ext.dcloud.net.cn/plugin?id=12803)下载集成

                let text = "NFC-NewText-123";
                let language = "zh"
                let langBytes = ASCIIUtils.encodeUtf8(language)
                // 文本转换为UTF-8格式
                let textBytes = ASCIIUtils.encodeUtf8(text)
                // 状态字节编码最高位数为0
                let utfBit = 0
                // 状态字节
                let status = utfBit + langBytes.length
                // byteArray为字节数组

                let payload = []
                payload.push(status)
                payload.push(...langBytes)
                payload.push(...textBytes)

                console.log(status)
                console.log(langBytes)
                console.log(textBytes)
                console.log(payload)
                var ndefRecords = []
                let tnf = 0x01
                // 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

                let type = [0x54]
                // 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"
                // "android.com:pkg".getBytes(): RTD_ANDROID_APP

                let id = [0x00]

                ndefRecords.push({
                    tnf: tnf, // 
                    type: type,
                    id: id,
                    payload: payload
                })
                var params = {
                    "records": ndefRecords
                }
                let result = writeNFCNdefRecords(params)
                this.showMsg(JSON.stringify(result))
  • NFC写入url数据(仅支持Android)

                var urls = []
                urls.push("http://www.baidu.com")
                var params = {
                    "urls": urls
                }
                let result = writeNFCNdefUrls(params)
                this.showMsg(JSON.stringify(result))
  • NFC写入包名package数据,刷卡即可打开app,(仅支持Android)

                var packages = []
                // 要打开的app包名,如打开短信:com.android.mms
                packages.push("com.android.mms")
                var params = {
                    "packages": packages
                }
                let result = writeNFCNdefPackages(params)
                this.showMsg(JSON.stringify(result), this.isFormat)
  • NFC写入非ndef数据,仅支持Android

var datas = []
datas.push({
    pageOffset: this.pageOffset,
    data: [0x00, 0x01, 0x02, 0x03]
})
var params = {
    "datas": datas
}
let result = writeNFCMifareUltralight(params)
this.showMsg(JSON.stringify(result))
  • 清除app获取到的NFC数据(仅支持Android,清除后需要再次刷卡才能识别到NFC)

cleanIntentNFCData();

ios NFC读写

ios里nfc的接口分为2种,一种是Tag,一种是NDEF,分别对应:


let tagReaderSession = new UTSNFCTagReaderSession()
let ndefReaderSession = new UTSNFCNDEFReaderSession()
  • 设置Tag回调,读写tag的代码都在这个回调里,参考demo

tagReaderSession.setCallback((resp) => {
    this.showMsg(JSON.stringify(resp))
    let opt = resp.opt
    switch (opt) {
        case "didBecomeActive":
            break;
        case "didInvalidate":
            // 结束操作
            tagReaderSession.invalidate()
            break;
        case "didDetectTags":
            let tags = resp.tags
            if (tags.length > 0) {
                // 连接tag
                let params = {}
                params.tagIndex = this.tagIndex // 连接第几个tag, tag在数组中的索引位置
                tagReaderSession.connectTag(params, (resp) => {
                    this.showMsg("connectTag:" + JSON.stringify(resp))
                    if (resp.flag) { // 连接成功
                        let statusParams = {}
                        statusParams.tagIndex = this.tagIndex

                        // 读数据
                        tagReaderSession.readNDEF(statusParams, (ndefResp) => {
                            tagReaderSession.invalidate()
                            this.showMsg("readNDEF:" + JSON.stringify(ndefResp))
                        })

                        // 查询状态  
                        // tagReaderSession.queryNDEFStatus(statusParams, (statusResp) => {
                        //  this.showMsg("queryNDEFStatus:" + JSON.stringify(resp))
                        //  if (statusResp.flag) { // 状态查询成功
                        //      // 1: NotSupported 2: ReadWrite 3:ReadOnly
                        //      let status = statusResp.status
                        //      if (status == 2) {
                        //          // 根据业务需要,往卡里写入数据
                        //          let writeParams = this.getWriteNDEFData()
                        //          writeParams.tagIndex = this.tagIndex
                        //          tagReaderSession.writeNDEF(writeParams, (
                        //              writeResp) => {
                        //              if (writeResp.flag) { // 数据写入成功
                        //                  this.showMsg("数据写入成功")
                        //              }
                        //              tagReaderSession.invalidate()
                        //          })
                        //      } else {
                        //          tagReaderSession.invalidate()
                        //      }
                        //  } else {
                        //      tagReaderSession.invalidate()
                        //  }
                        // })
                    } else { // 连接失败
                        tagReaderSession.invalidate()
                    }
                })
            } else {
                tagReaderSession.invalidate()
            }
            break;
        default:
            break;
    }
})
  • 设置NDEF回调,读写ndef的代码都在这个回调里,参考demo

ndefReaderSession.setCallback((resp) => {
    this.showMsg(JSON.stringify(resp))
    let opt = resp.opt
    switch (opt) {
        case "didBecomeActive":
            break;
        case "didInvalidate":
            // 结束操作
            ndefReaderSession.invalidate()
            break;
        case "didDetectNDEFs":

            ndefReaderSession.invalidate()
            break;
        case "didDetectTags":
            let tags = resp.tags
            if (tags.length > 0) {
                ndefReaderSession.invalidate()
                // 连接tag
                let params = {}
                params.tagIndex = this.tagIndex // 连接第几个tag, tag在数组tags中的索引位置
                // ndefReaderSession.connectTag(params, (resp) => {
                //  if (resp.flag) { // 连接成功
                //      // 查询状态
                //      // this.iosQueryStatus()
                //      let writeParams = this.getWriteNDEFData()
                //      writeParams.tagIndex = this.tagIndex
                //      ndefReaderSession.writeNDEF(writeParams, (writeResp) => {
                //          if (writeResp.flag) { // 数据写入成功
                //              this.showMsg("数据写入成功")
                //          }
                //          ndefReaderSession.invalidate()
                //      })
                //  } else { // 连接失败
                //      ndefReaderSession.invalidate()
                //  }
                // })
            } else {
                ndefReaderSession.invalidate()
            }
            break
        default:
            break;
    }
})
  • 判断iOS NFC是否可用

        let readingAvailable = UTSNFCReaderWriter.readingAvailable
        if (readingAvailable) {

        }
  • 开始Tag会话

// 初始化
tagReaderSession.initSession({
    pollingOptions: 1 // 1: ISO14443 2: ISO15693 4: ISO18092 8: PACE
})

// 设置提示语
tagReaderSession.setAlertMessage("Tag 读取中...")

// 开始识别
tagReaderSession.begin()
  • 结束tag会话

tagReaderSession.invalidate()
  • 连接Tag

let params = {}
params.tagIndex = this.tagIndex // 连接第几个tag, tag在数组中的索引位置
tagReaderSession.connectTag(params, (resp) => {

})
  • 读取Tag数据

let statusParams = {}
statusParams.tagIndex = this.tagIndex
// 读数据
tagReaderSession.readNDEF(statusParams, (ndefResp) => {
    tagReaderSession.invalidate()
    this.showMsg("readNDEF:" + JSON.stringify(ndefResp))
})
  • 查询Tag状态

tagReaderSession.queryNDEFStatus(statusParams, (statusResp) => {})
  • 写入数据

let writeParams = this.getWriteNDEFData()
writeParams.tagIndex = this.tagIndex
tagReaderSession.writeNDEF(writeParams, (
    writeResp) => {
    if (writeResp.flag) { // 数据写入成功
        this.showMsg("数据写入成功")
    }
    tagReaderSession.invalidate()
})
  • 开始识别NDEF

// 初始化
ndefReaderSession.initSession({
    invalidateAfterFirstRead: false,
    alertMessage: "NDEF 读取中..."
})
// 开始识别
ndefReaderSession.begin()
  • 结束会话

ndefReaderSession.invalidate()
  • 连接tag

let params = {}
params.tagIndex = this.tagIndex // 连接第几个tag, tag在数组tags中的索引位置
ndefReaderSession.connectTag(params, (resp) => {})
  • 查询状态

let params = {}
params.tagIndex = this.tagIndex
tagReaderSession.queryNDEFStatus(params, (resp) => {
    if (resp.flag) { // 状态查询成功
        // 1: NotSupported 2: ReadWrite 3:ReadOnly
        let status = resp.status
        if (status == 2) {
            // 根据业务需要,往卡里写入数据
            this.iosWriteData()
        } else {
            tagReaderSession.invalidate()
        }
    } else {
        tagReaderSession.invalidate()
    }
})
  • 写入数据

let writeParams = this.getWriteNDEFData()
writeParams.tagIndex = this.tagIndex
ndefReaderSession.writeNDEF(writeParams, (writeResp) => {
    if (writeResp.flag) { // 数据写入成功
        this.showMsg("数据写入成功")
    }
    ndefReaderSession.invalidate()
})

隐私、权限声明

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

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

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

插件不采集任何数据

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

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问