一、NFC简介
NFC使用示例NFC(Near Field Communication,NFC)近场通信,是一种短距高频的无线电技术,在13.56MHz频率运行于10厘米距离内。其传输速度有106 Kbit/秒、212 Kbit/秒或者424 Kbit/秒三种。
使用了NFC技术的设备(比如手机)可以在彼此靠近的情况下进行数据交换,是由非接触式射频识别(RFID)及互连互通技术整合演变而来,通过在单一芯片上集成感应式读卡器、感应式卡片和点对点通信的功能,利用移动终端实现移动支付、电子票务、门禁、移动身份识别、防伪等应用。目前,苹果的CoreNFC对NFC的格式支持有限,暂时仅支持NDEF
格式。
二、集成NFC
1、配置开发证书
在开发者账号里面添加上对NFC的支持,只需要配置App ID支持NFC,更新Provisioning Profiles
开发证书配置.jpg2、配置Capabilitles
在XCode中添加TARGETS
->Capabilities
中打开Near Field Communication Tag Reading
选项,XCode会自动帮你添加其他步骤
3、Info中配置NFC使用说明
在Info.plist
文件中添加Privacy - NFC Scan Usage Description
。这里使用的描述信息会显示在读取界面中
4、代码实现
#import "ViewController.h"
#import <CoreNFC/CoreNFC.h>
@interface ViewController () <NFCNDEFReaderSessionDelegate>
@end
@implementation ViewController
// 开始扫描
- (IBAction)startScan {
/*
条件:iphone7/7plus运行iOS11及以上
invalidateAfterFirstRead 属性表示是否需要识别多个NFC标签,
如果是YES,则会话会在第一次识别成功后终止。否则会话会持续。
不过有一种例外情况,就是如果响应了-readerSession:didInvalidateWithError:方法,
则是否为YES,会话都会被终止
*/
NFCNDEFReaderSession *session =
[[NFCNDEFReaderSession alloc] initWithDelegate:self
queue:nil
invalidateAfterFirstRead:YES];
// 开始扫描
[session beginSession];
}
// 扫描到的回调
-(void)readerSession:(NFCNDEFReaderSession *)session didDetectNDEFs:(NSArray<NFCNDEFMessage *> *)messages{
/*
数组messages中是NFCNDEFMessage对象
NFCNDEFMessage对象中有一个records数组,这个数组中是NFCNDEFPayload对象
参考NFCNDEFMessage、NFCNDEFPayload类
*/
for (NFCNDEFMessage *message in messages) {
for (NFCNDEFPayload *record in message.records) {
NSString *dataStr = [[NSString alloc] initWithData:record.payload
encoding:NSUTF8StringEncoding];
NSLog(@"扫描结果 :%@", dataStr);
}
}
// 主动终止会话,调用如下方法即可。
[session invalidateSession];
}
// 错误回调
- (void)readerSession:(NFCNDEFReaderSession *)session didInvalidateWithError:(NSError *)error{
// 识别出现Error后会话会自动终止,此时就需要程序重新开启会话
NSLog(@"错误回调 : %@", error);
}
@end
Demo下载:https://github.com/ZhangJingHao/ZJHNFCDemo
扫描结果详解,请参考下文NDEF详解
5、注意事项
- NFC最低支持硬件iPhone7或者iPhone7Plus,最低支持系统为iOS11
- 需要开启一个session,与其他session类似,同时只能开启一个
- 需要App完全在前台模式,iPhoneX之后机型,支持后台读取
- 每个session最多扫描60s,超时需再次开启新session
- 配置读取单个或多个Tag,配置为单个时,会在读取到第一个Tag时自动结束session
三、API详解
1、会话
1.1 NFCReaderSession
// 是否已经启动并准备使用
@property(nonatomic, getter=isReady, readonly) BOOL ready;
// 开始会话
- (void)beginSession;
// 关闭会话
- (void)invalidateSession;
// 告知用户在程序中使用NFC的说明,当扫描时该信息将显示给用户
// 该属性与NFCReaderUsageDescription键中的不一样
@property(nonatomic, copy) NSString *alertMessage;
1.2 NFCNDEFReaderSession
从类的名称可以看出,这个类是用来读取NDEF格式数据的会话的。继承于NFCReaderSession
根据文档显示,一次只能在系统中激活一个NFC NDEF会话,如果创建多个的话,系统会将其放在队列中,按照顺序执行
/* 初始化会话
* delegate 会话的回调
* queue 线程
* invalidateAfterFirstRead 第一NDEF读取完毕后是否停止会话
*
* 当会话为读取多个NDEF时,每次读取成功都会通过代理回调
* 读取多个NDEF时,会话会一直到调用终止活动API或者超时等才会停止会话
*
* 如果第一个NDEF读取成功便自动终止,这种情况下,通过readerSession:didInvalidateWithError:回调,此时状态为NFCReaderSessionInvalidationErrorFirstNDEFTagRead
*/
- (instancetype)initWithDelegate:(id<NFCNDEFReaderSessionDelegate>)delegate queue:(dispatch_queue_t)queue invalidateAfterFirstRead:(BOOL)invalidateAfterFirstRead;
// 设备是否支持NFC读取标签
@property(class, nonatomic, readonly) BOOL readingAvailable;
// 读取到信息时回调
- (void)readerSession:(NFCNDEFReaderSession *)session didDetectNDEFs:(NSArray<NFCNDEFMessage *> *)messages;
// 发生错误或者无效时回调
- (void)readerSession:(NFCNDEFReaderSession *)session didInvalidateWithError:(NSError *)error;
2、标签
2.1 NFCTag
// 检测到的标签是否可用
@property(nonatomic, getter=isAvailable, readonly) BOOL available;
// 获取会话
@property(nonatomic, weak, readonly) id<NFCReaderSession> session;
// 标签类型
@property(nonatomic, readonly, assign) NFCTagType type;
2.2 NFCTagCommandConfiguration
// 最大的重置次数,最多为256,默认为0
@property(nonatomic, assign) NSUInteger maximumRetries;
// 重试的时间间隔,默认为0
@property(nonatomic, assign) NSTimeInterval retryInterval;
3、NDEFMessage
3.1 NFCNDEFPayload 消息载体
// 标识,由NDEF规范定义
@property(nonatomic, copy) NSData *identifier;
// 载体中的数据,由NDEF规范定义
@property(nonatomic, copy) NSData *payload;
// 载体的类型,由NDEF规范定义
@property(nonatomic, copy) NSData *type;
// 载体的类型名称格式,由NDEF规范定义
@property(nonatomic, assign) NFCTypeNameFormat typeNameFormat;
NFCTypeNameFormat定义的类型:
TypeNameFormatAbsoluteURI 统一使用资源标识标准
NFCTypeNameFormatEmpty 空信息
NFCTypeNameFormatMedia RFC 2046 定义的媒体类型
NFCTypeNameFormatNFCExternal
NFCTypeNameFormatNFCWellKnown
NFCTypeNameFormatUnchanged
NFCTypeNameFormatUnknown 未知
3.2 NFCNDEFMessage
// NFCNDEFPayload数组
@property(nonatomic, copy) NSArray<NFCNDEFPayload *> *records;
4、Errors
typedef NS_ERROR_ENUM(NFCErrorDomain, NFCReaderError) {
NFCReaderErrorUnsupportedFeature = 1, // 不支持此功能
NFCReaderErrorSecurityViolation, // 安全问题
NFCReaderTransceiveErrorTagConnectionLost = 100, // 标签连接丢失
NFCReaderTransceiveErrorRetryExceeded, // 重连次数过多
NFCReaderTransceiveErrorTagResponseError, // 标签响应错误
NFCReaderSessionInvalidationErrorUserCanceled = 200, // 用户取消会话
NFCReaderSessionInvalidationErrorSessionTimeout, // 会话时间超时
NFCReaderSessionInvalidationErrorSessionTerminatedUnexpectedly, // 会话意外终止
NFCReaderSessionInvalidationErrorSystemIsBusy, // 系统正忙,会话失败
NFCReaderSessionInvalidationErrorFirstNDEFTagRead, // 读取的第一个NDEF
NFCTagCommandConfigurationErrorInvalidParameters = 300, // 标签配置无效参数
};
四、NDEF详解
1、NDEF简介
NDEF(NFC Data Exchange Format)是一种能够在NFC设备或者标签之间进行信息交换的数据格式。NDEF格式由各种 NDEF Messages 和 NDEF Records 组成。NDEF格式使用了一种容易理解的格式来存储和交换信息,如:URI、纯文本等等。
NFC标签,像Mifare Classic卡片可以配置为NDEF标签,通过一个NFC设备写入的数据可以被其他NDEF兼容的设备访问。NDEF消息还可以用于两个活跃的NFC设备之间“点对点”模式交换数据。
NFC有五种的类型.png2、字段详解
NDEF Messages (NFCNDEFMessage)
NDEF Messages是NDEF Records交换机制的基础,每一个message包含一个或多个records。
NDEF Records (NFCNDEFPayload)
NDEF Records包含一个特定的payload,并且有以下结构来标识内容和记录大小:
NDEF Records.jpegRecord Header(记录头)
记录头包含了很多重要的信息,它占用3个位来标识遵循TNF协议的记录的类型,讲人话就是这3个位用来表示这条记录的类型的。
TNF: Type Name Format 字段
一条NDEF记录的类型名称是一个3个位的数值,用来描述这条记录的类型,并且可以用来设置对该记录中其它的结构和内容的期望。简单的说就是这3个位不仅可以表示该条记录的类型,也可以在一定程度上决定了该条记录接下来的数据结构。可能的记录名称如下表:
Record Type (NFCTypeNameFormat)
0x00 (NFCTypeNameFormatEmpty)
Empty Record 表明这条记录没有类型、id或有效payload。这个记录类型一般用于新格式化的NDEF卡上,因为NDEF标签必须有至少一个NDEF纪录。
0x01 (NFCTypeNameFormatNFCWellKnown)
Well-Known Record 表明记录类型字段使用RTD类型名称格式。这种类型名称用一个Record Type Definition (RTD)来存储任何指定的类型,例如:存储RTD文本、RTD URIs等等。同时,这是一种比较常用的也比较有用的记录类型。
0x02 (NFCTypeNameFormatMedia)
MIME Media Record 表明payload是这条NDEF记录分块的中间或者最后一块。
0x03 (NFCTypeNameFormatAbsoluteURI)
Absolute URI Record 表明这条记录的类型字段一定包含一个URI字段。
0x04 (NFCTypeNameFormatNFCExternal)
External Record 表明这条记录的类型字段包含一个RTD格式的外部字段。
0x05 (NFCTypeNameFormatUnknown)
Unknown Record 表明payload的类型未知。
0x06 (NFCTypeNameFormatUnchanged)
Unchanged Record 未发生变化的记录类型,释同MIME Media Record。
类型示例.jpeg其他字段
IL:ID Length 字段
IL是ID长度的标志位,用来表示下面的ID Length字段是否省略。如果这个位设置为0,则该条记录中省略ID Length。
SR:Short Record 位
短记录标志位,如果下面的PAYLOAD LENGTH字段小于等于一个字节,则该位设置为1 。
CF:Chunk Flag
块标识位,用于标识当前块是第一个记录块还是中间的记录块。
ME:Message End
结束标志位,用于标识当前记录是否是当前Message的最后一条记录。
MB:Message Begin
起始标志位,用于标识当前记录是否是当前Message的第一条记录。
Type Length
表示类型字段的长度。对于上面TNF字段描述中的某些值,该字段一直为0 。
Payload Length
表示该条记录的payload字段长度。如果上面的SR字段设置为1,则该字段占用1个字节的长度,但是如果SR设置为0,则该字段将有32个位,占用4个字节的长度。
ID Length
表示该条记录的ID的长度。只有当上面的IL位置1时该字段才会被省略。
Record Type
表示记录的类型,这个字段的值必须根据TNF位的设置确定。
Record ID
表示该条记录的ID。当IL位为0时,该字段省略。
Payload
表示该条记录的payload,该字段的长度务必与上面的Payload Length字段值一致。
3、Well-Known Records 和 URI Records
首先要说的就是这两个概念的区别,Well-Known Records(TNF Record Type 0x01) 是最常用也是最有用的NFC记录类型,它是写在上面说到的TNF字段的三个位里的,它描述的是当前这条NDEF记录的整体类型,相当于一个总的架构决策。
而 URI Records(0x55/‘U’)是比较有用的数据类型,它是写在上面 Recode Type 字段的一个字节(8个位)里的,它描述的是这条NDEF记录携带的数据信息类型,简单的说就是这条记录携带了什么样的信息。
这里,关于这个URI Records我要多说几句,这个类型可以用来存储例如电话号码、网站地址以及各种协议的链接等等很多有用的信息,它的结构定义如下:
URI Records结构定义.jpeg第一个字节表示该类型的识别码,这个识别码的主要是用于缩短URI的长度,它的有效值详见下表:
URI Records具体数据.jpeg后面的N个字节就是用来表示一个URI去掉前面识别码之后剩余的部分,举个例子:例如我们要将 https://www.baidu.com
写入,则在第一个字节里我们要写入的是 0x02
,表示 https://www.
。
五、后台读取NFC标签
iPhone XS及更高版本支持后台标签读取。系统扫描并读取NFC数据,而无需用户使用应用程序扫描标签。每次读取新标签时,系统都会显示弹出通知。用户点击通知后,系统会将标记数据传递到相应的应用程序。如果iPhone被锁定,系统会在将标签数据提供给应用程序之前提示用户解锁手机。
系统以与通用链接相同的方式处理包含URL方案的URI的NDEF有效负载。系统在读取标签后显示通知。当用户点击通知时,系统会启动支持URL方案的应用程序。
后台标记读取支持以下URL方案:
后台读取NFC.png
通用链接的配置与使用,请移步这篇文章:iOS 通用链接(Universal Links)的配置与使用
参考链接:
Demo下载
技术干货 | iOS 11 NFC技术
iOS11 Core NFC
Adding Support for Background Tag Reading
网友评论