iOS-NFC技术

2,866 阅读4分钟

先了解几个概念 什么是NFC? NDEF指的是什么? 什么是CoreNFC?

NFC框架

什么是 NFC

NFC(Near Field Communication)即近距离无线通讯技术。该技术由飞利浦公司和索尼公司共同开发,可以在移动设备、消费类电子产品、PC 和智能控件工具间进行近距离无线通信。NFC提供了一种简单、触控式的解决方案,可以让消费者简单直观地交换信息、访问内容与服务。 NFC通信技术,允许电子设备之间进行非接触式点对点数据传输(在十厘米内)交换数据。这个技术由免接触式射频识别(RFID)演变而来,并向下兼容RFID,主要用于手机等手持设备中提供M2M(Machine to Machine)的通信。由于近场通讯具有天然的安全性。 NFC是一种短距高频的无线电技术,在13.56MHz频率运行于10厘米距离内。其传输速度有106 Kbit/秒、212 Kbit/秒或者424 Kbit/秒三种。目前近场通信已通过成为ISO/IEC IS 18092国际标准、ECMA-340标准与ETSI TS 102 190标准。NFC采用主动和被动两种读取模式。

NDEF指的是什么?

NFC Data Exchange Format : NFC数据交换格式,NFC组织约定的NFC tag中的数据格式。 NDEF是轻量级的紧凑的二进制格式,可带有URL、vCard和NFC定义的各种数据类型。 NDEF的由各种数据记录组成,而各个记录由报头(Header)和有效载荷(Payload)组成,其中NDEF记录的数据类型和大小由记录载荷的报头注明,这里的报头包含3部分,分别为Length、Type和Identifier.。 NFC Data Exchange Format : NFC数据交换格式,NFC组织约定的NFC tag中的数据格式。

什么是CoreNFC?

CoreNFC是苹果推出的支持NFC通讯的框架,CoreNFC读取的是NDEF标签的数据。

支持的设备:

iOS开发

plist配置: Privacy - NFC Scan Usage Description

分析:

引入NFC库 #import <CoreNFC/CoreNFC.h> 执行协议

**注意⚠️:读取功能iOS11系统开始可以使用,写入功能iOS13系统才可以使用。 **

NFCNDEFReaderSession *session;//nfc会话

NFCNDEFMessage *message;//扫描的消息体结构

//MARK:开始扫描nfc

-(void)startByNFCScaning:(void(^)(NSString *))finishBlock{

	if (!NFCNDEFReaderSession.readingAvailable) {

		UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"扫不到" message:@"设备不支持" preferredStyle:UIAlertControllerStyleAlert];

		[alertVC addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];

		[self.vc presentViewController:alertVC animated:YES completion:nil];

		return;

	}

	if (self.session) {

		NSLog(@"nfc已经存在");

		return;

	}

	self.isWrite = NO;

	self.session = [[NFCNDEFReaderSession alloc]initWithDelegate:self queue:nil invalidateAfterFirstRead:NO];

	self.session.alertMessage = @"把你的iPhone放在一个NDEF标签附近读信息。";

	[self.session beginSession];

	NSLog(@"开始扫描");

	self.finishBlock = finishBlock;

}
-(void)writeByNFCScaning:(NSString *)message {

	if (!NFCNDEFReaderSession.readingAvailable) {

		UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"扫不到" message:@"设备不支持" preferredStyle:UIAlertControllerStyleAlert];

		[alertVC addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];

		[self.vc presentViewController:alertVC animated:YES completion:nil];

		return;

	}

	if (self.session) {

		NSLog(@"nfc已经存在");

		return;

	}

	self.isWrite = YES;

	self.writeMessage = message;

	self.session = [[NFCNDEFReaderSession alloc]initWithDelegate:self queue:nil invalidateAfterFirstRead:false];

	self.session.alertMessage = @"把你的iPhone放在一个NDEF标签附近写信息。";

	[self.session beginSession];

	NSLog(@"开始写入");


}
//MARK:NFCNDEFReaderSessionDelegate
//error
- (void)readerSession:(NFCNDEFReaderSession *)session didInvalidateWithError:(NSError *)error{
	if (error &&
		error.code != NFCReaderSessionInvalidationErrorFirstNDEFTagRead &&
		error.code != NFCReaderSessionInvalidationErrorUserCanceled) {
		dispatch_async(dispatch_get_main_queue(), ^{
			UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"通信错误" message:error.description?:@"异常" preferredStyle:UIAlertControllerStyleAlert];
			[alertVC addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
			[self.vc presentViewController:alertVC animated:YES completion:nil];
		});
	}
	self.session = nil;
}

//message
-(void)readerSession:(NFCNDEFReaderSession *)session didDetectNDEFs:(NSArray<NFCNDEFMessage *> *)messages{
    self.session.alertMessage = @"识别成功";
	[self.session invalidateSession];
	dispatch_async(dispatch_get_main_queue(), ^{
		self.message = messages.firstObject;
		[self dataHandle];
	});
}

//新方法
//新message
- (void)readerSession:(NFCNDEFReaderSession *)session didDetectTags:(NSArray<__kindof id<NFCNDEFTag>> *)tags API_AVAILABLE(ios(13.0)){
	if (tags.count > 1) {
		self.session.alertMessage = @"检测到多个标记,请删除所有标记并重试。";
		[self tagRemovalDetect:tags.firstObject];
		return;
	}
	id<NFCNDEFTag> tag = tags.firstObject;
	[self.session connectToTag:tag completionHandler:^(NSError * _Nullable error) {
		if (error != nil) {
			[self.session restartPolling];
			return;
		}
		//查询状态
		[tag queryNDEFStatusWithCompletionHandler:^(NFCNDEFStatus status, NSUInteger capacity, NSError * _Nullable error) {
			if (error != nil) {
				[self.session invalidateSessionWithErrorMessage:@"无法确定NDEF状态。请再试一次。"];
				return;
			}
			if (self.isWrite) {//写入
				if (status == NFCNDEFStatusReadOnly) {
					[self.session invalidateSessionWithErrorMessage:@"标记不可写。"];
					return;
				}else if (status == NFCNDEFStatusReadWrite){
					if (self.message.length > capacity) {
						[self.session invalidateSessionWithErrorMessage:@"标签容量太小。最小大小要求是\(self.ndefMessage!.length)字节。"];
						return;
					}
					[tag writeNDEF:self.message completionHandler:^(NSError * _Nullable error) {
						if (error != nil) {
							[self.session invalidateSessionWithErrorMessage:@"更新标签失败了。请再试一次。"];
						}else{
							self.session.alertMessage = @"更新成功!";
							[self.session invalidateSession];
						}
					}];
				} else {
					[self.session invalidateSessionWithErrorMessage:@"标签不是NDEF格式的。"];
				}

			}else{//读取
				if (status == NFCNDEFStatusNotSupported) {
					self.session.alertMessage = @"标签不兼容NDEF";
					[self.session invalidateSession];
					return;
				}

				[tag readNDEFWithCompletionHandler:^(NFCNDEFMessage * _Nullable message, NSError * _Nullable error) {
					NSString *statusMessage;
					if (nil != error || nil == message) {
						statusMessage = @"读取NDEF from标签失败";
					}else{
                        statusMessage = @"识别成功";
						dispatch_async(dispatch_get_main_queue(), ^{
							self.message = message;
							[self dataHandle];
						});
					}
					self.session.alertMessage = statusMessage;
					[self.session invalidateSession];
				}];
			}
		}];
	}];
}

//写入
- (void)readerSessionDidBecomeActive:(NFCNDEFReaderSession *)session API_AVAILABLE(ios(13.0)){
	if (self.isWrite) {
		NFCNDEFPayload *urlPayload = [NFCNDEFPayload wellKnownTypeURIPayloadWithString:self.writeMessage];
		NSLog(@"初始化写入数据:%@",self.writeMessage);
		self.message = [[NFCNDEFMessage alloc]initWithNDEFRecords:@[urlPayload]];
		NSLog(@"MessageSize=%d",self.message.length);
	}
}

//解析数据处理

-(void)dataHandle{

	NFCNDEFPayload *payload = self.message.records.firstObject;

	NSString *title = [[NSString alloc]initWithData:payload.payload encoding:NSUTF8StringEncoding];

	NSString *type = [[NSString alloc]initWithData:payload.type encoding:NSUTF8StringEncoding];

	if (@available(iOS 13.0, *)) {

		switch (payload.typeNameFormat) {

			case NFCTypeNameFormatNFCWellKnown:{

				if (type != nil) {

					NSURL *url = [payload wellKnownTypeURIPayload];

					if (url.absoluteString != nil) {

						title = url.absoluteString;

					}else{

						title = title;

					}

				}

			}

				break;

			case NFCTypeNameFormatAbsoluteURI:{

				if (title != nil) {

					title = title;

				}

			}

				break;

			case NFCTypeNameFormatMedia:{

				if (type != nil) {

					title = type;

				}

			}

				break;

			case NFCTypeNameFormatNFCExternal:

			case NFCTypeNameFormatEmpty:

			case NFCTypeNameFormatUnknown:

			case NFCTypeNameFormatUnchanged:{

				title = @"...";

			}

				break;

			default:

				title = @"...";

				break;

		}

		title = [title componentsSeparatedByString:@","].lastObject;

	}

	if (self.finishBlock) {

		self.finishBlock(title);

	}




}




//轮询

-(void)tagRemovalDetect:(id<NFCNDEFTag>)tag API_AVAILABLE(ios(13.0)){

	[self.session connectToTag:tag completionHandler:^(NSError * _Nullable error) {

		if (error != nil || !tag.isAvailable) {

			NSLog(@"Restart polling");

			[self.session restartPolling];

			return;

		}

		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(500 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

			[self tagRemovalDetect:tag];

		});

	}];

}