Core NFC在iOS 11中引入,用于处理NFC阅读Tag。由于目前只开放了读的权限,所以Core NFC是非常轻量级的一个框架。

1688625-0962d5881300e923.png
上图是WWDC上苹果提供的NFC技术细分图,由于苹果支持所有格式的Tag,所以只需做大致了解即可。
使用NFC前需要注意一下几点:
- 需要开启一个session,与其他session类似,同时只能开启一个
- 需要App完全在前台模式(2018年以后发布的手机,支持后台调用NFC功能)
- 每个session最多扫描60s,超时需再次开启新session
- 配置读取单个或多个Tag,配置为单个时,会在读取到第一个Tag时自动结束session
- 隐私描述(后文会写到如何配置)会在扫描页面显示
使用NFC,第一步需要配置Capabilities,这会自动为你生成entitlements文件中的必要配置。同时为你的App ID激活相关功能。

1688625-83d1b1d419bc66c1.png
iOS 13之后的配置发生了更改。(如果你的APP是从低版本升级上来的需要添加新字段,否则上架AppStore会校验失败。)
Invalid entitlement for core nfc framework. The sdk version '13.0' and min OS version '9.0' are not compatible for the entitlement 'com.apple.developer.nfc.readersession.formats' because 'TAG is missing in the entitlement'.

image.png
第二步需要打开隐私相关设置,向info.plist中添加Privacy - NFC Scan Usage Description。

image.png
可以完全按照如下代码设置。此为读取标签的公共区域。
之后会提供读取UID的demo。
#import <CoreNFC/CoreNFC.h>
@interface ViewController ()<NFCNDEFReaderSessionDelegate>
@property (nonatomic ,strong)UIButton *beginButton;
@property (nonatomic ,strong)NFCNDEFReaderSession *session;
@property (nonatomic ,strong)UILabel *textLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self createUI];
}
- (void)viewDidAppear:(BOOL)animated{
[self beginSession];
}
- (void)createUI{
[self.view addSubview:self.beginButton];
[self.view addSubview:self.textLabel];
}
- (void)viewDidDisappear:(BOOL)animated{
self.session = nil;
}
- (void)beginSession{
if (@available(iOS 11.0, *) && [NFCNDEFReaderSession readingAvailable]) {
self.session.alertMessage = NSLocalizedString(@"将标签放到手机背面", @"");
[self.session beginSession];
} else {
NSLog(NSLocalizedString(@"此设备不支持NFC", @""));
}
}
- (void)buttonAlick:(UIButton *)button{
if (button == self.beginButton) {
[self.session beginSession];
}
}
- (void)readerSession:(NFCNDEFReaderSession *)session didDetectNDEFs:(NSArray<NFCNDEFMessage *> *)messages API_AVAILABLE(ios(11.0)){
if (@available(iOS 11.0, *)) {
for (NFCNDEFMessage *message in messages) {
for (NFCNDEFPayload *payload in message.records) {
[self.session invalidateSession];
dispatch_async(dispatch_get_main_queue(), ^{
//在主线程里开启事件
NSString *nfcString = [[[NSString alloc] initWithData:payload.payload encoding:NSUTF8StringEncoding] substringFromIndex:1];//因为扫出来的数据多\^C 所以要截取所需要的字符串
self.textLabel.text = nfcString;
});
}
}
} else {
// Fallback on earlier versions
}
}
- (void)readerSession:(NFCNDEFReaderSession *)session didInvalidateWithError:(NSError *)error API_AVAILABLE(ios(11.0)){
self.session = nil;
}
- (NFCNDEFReaderSession *)session API_AVAILABLE(ios(11.0)){
if (!_session) {
_session = [[NFCNDEFReaderSession alloc] initWithDelegate:self queue:dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT) invalidateAfterFirstRead:NO];
}
return _session;
}
- (UIButton *)beginButton{
if (!_beginButton) {
_beginButton = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 200, 30)];
[_beginButton setTitle:NSLocalizedString(@"点击开始扫描标签", @"") forState:UIControlStateNormal];
_beginButton.titleLabel.font = [UIFont systemFontOfSize:17];
_beginButton.backgroundColor = [UIColor blueColor];
[_beginButton addTarget:self action:@selector(buttonAlick:) forControlEvents:UIControlEventTouchUpInside];
}
return _beginButton;
}
- (UILabel *)textLabel{
if (!_textLabel) {
_textLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 200, 200, 80)];
_textLabel.textColor = [UIColor whiteColor];
_textLabel.backgroundColor = [UIColor grayColor];
_textLabel.font = [UIFont systemFontOfSize:15];
_textLabel.numberOfLines = 0;
}
return _textLabel;
}
iOS 13以后注意:不可以设置其他协议方法
例如:
//此方法会在iOS13之后会屏蔽掉NFCNDEFReaderSessionDelegate 的协议方法。导致调用失败
- (void)readerSession:(NFCReaderSession *)session didDetectTags:(NSArray<__kindof id<NFCTag>> *)tags