#一.说明
1-苹果手机从iPhone6开始装有NFC硬件,但并未对第三方应用开放。因此iPhone6及iPhone6s不能识别NFC标签,但是可以使用系统NFC功能如:刷地铁。
2-苹果从iOS11系统开始开放NFC读取功能,同时要求iPhone7及以上机型。不满足要求则无法读取NFC标签
3-苹果在iOS13系统开放了标签写入功能,想要向标签内写入数据,需要升级系统到iOS13,同样只能写入DNEF格式数据
4-另外苹果只开放DNEF数据格式的NFC标签读取,如果数据格式不满足则无法读取。身份证、地铁卡、银行卡、大部分的工卡都不是DNEF格式,因此无法读取。(空标签只能在iOS13系统下才可以读取到)(NFC标签可以去淘宝购买,价格很便宜 9.9六个还包邮)
5-身份证、地铁卡虽然无法读取到数据,但是可以用苹果官方APP“快捷指令”进行标记,来实现一些新颖玩法(需要iPhoneXS以上机型)
6-关于后台读取,iPhoneXS以上机型支持,屏幕点亮状态下(无需解锁),手机可以读取一些特定数据格式的NFC标签。识别到标签后,可以实现拨打电话,发送邮件等功能(需解锁)
#二.项目推荐
我自己写的一个关于NFC读写的APP,可以直接用来调试NFC标签
https://itunes.apple.com/cn/app/id1492298987
#三.工程配置
1.需要配置Capabilitles。使用NFC需要配置Capabilitles,这会自动为你生成entitlements文件中的必要配置。
2.打开隐私相关设置。向info.plist中添加Privacy - NFC Scan Usage Description。
3.激活App ID的相关功能。如下图所示(自动签名项目系统会自动配置)
#四.代码
1.NFCManager.h
#import <Foundation/Foundation.h>
#import <CoreNFC/CoreNFC.h>NS_ASSUME_NONNULL_BEGINtypedef NS_ENUM(NSInteger, NFCSupportsStatus) {NFCSupportStatusYes,//支持NFCSupportStatusDeviceNo,//硬件不支持NFCSupportStatusnSystemNo,//系统不支持
};API_AVAILABLE(ios(11.0))
typedef void(^NFCScanSuccessBlock)(NFCNDEFMessage *message);
typedef void(^NFCScanErrorBlock)(NSError *error);
typedef void(^NFCWriteSuccessBlock)(void);
typedef void(^NFCWritErrorBlock)(NSError *error);API_AVAILABLE(ios(11.0))
@interface NFCManager : NSObject
@property(nonatomic,copy)NFCScanSuccessBlock scanSuccessBlock;
@property(nonatomic,copy)NFCScanErrorBlock scanErrorBlock;
@property(nonatomic,copy)NFCWriteSuccessBlock writeSuccessBlock;
@property(nonatomic,copy)NFCWritErrorBlock writErrorBlock;
@property(nonatomic,assign) BOOL moreTag;//多标签识别+(NFCManager *)sharedInstance;
-(void)scanTagWithSuccessBlock:(NFCScanSuccessBlock)scanSuccessBlock andErrorBlock:(NFCScanErrorBlock)scanErrorBlock;
-(void)writeMessage:(NFCNDEFMessage *)message ToTagWithSuccessBlock:(NFCWriteSuccessBlock)writeSuccessBlock andErrorBlock:(NFCWritErrorBlock)writErrorBlock;
//判断是否支持读写功能
+(NFCSupportsStatus)isSupportsNFCReading;
+(NFCSupportsStatus)isSupportsNFCWrite;
//获取类型名数组
+(NSArray*)getNameFormatArray;
//获取类型名字
+(NSString*)getNameFormat:(NFCTypeNameFormat)typeName;
//获取类型结构体
+(NFCTypeNameFormat)getNFCTypeNameFormat:(NSString*)typeName;
@endNS_ASSUME_NONNULL_END
1.NFCManager.m
#import "NFCManager.h"API_AVAILABLE(ios(11.0))
@interface NFCManager ()<NFCNDEFReaderSessionDelegate>{BOOL isReading;
}
@property (strong, nonatomic) NFCNDEFReaderSession *session;
@property (strong, nonatomic) NFCNDEFMessage *message;
@end@implementation NFCManager#pragma mark - 单例方法
+(NFCManager *)sharedInstance{static dispatch_once_t onceToken;static NFCManager * sSharedInstance;dispatch_once(&onceToken, ^{sSharedInstance = [[NFCManager alloc] init];});return sSharedInstance;
}
-(void)scanTagWithSuccessBlock:(NFCScanSuccessBlock)scanSuccessBlock andErrorBlock:(NFCScanErrorBlock)scanErrorBlock{self.scanSuccessBlock=scanSuccessBlock;self.scanErrorBlock=scanErrorBlock;isReading=YES;[self beginScan];
}
-(void)writeMessage:(NFCNDEFMessage *)message ToTagWithSuccessBlock:(NFCWriteSuccessBlock)writeSuccessBlock andErrorBlock:(NFCWritErrorBlock)writErrorBlock{self.message=message;self.writeSuccessBlock=writeSuccessBlock;self.writErrorBlock=writErrorBlock;isReading=NO;[self beginScan];
}
+(NFCSupportsStatus)isSupportsNFCReading{if (@available(iOS 11.0,*)) {if (NFCNDEFReaderSession.readingAvailable == YES) {return NFCSupportStatusYes;}else{NSLog(@"%@",@"该机型不支持NFC功能!");return NFCSupportStatusDeviceNo;}}else {NSLog(@"%@",@"当前系统不支持NFC功能!");return NFCSupportStatusnSystemNo;}
}
+(NFCSupportsStatus)isSupportsNFCWrite{if (@available(iOS 13.0,*)) {if (NFCNDEFReaderSession.readingAvailable == YES) {return NFCSupportStatusYes;}else{NSLog(@"%@",@"该机型不支持NFC功能!");return NFCSupportStatusDeviceNo;}}else {NSLog(@"%@",@"当前系统不支持NFC功能!");return NFCSupportStatusnSystemNo;}
}
-(void)beginScan{if (@available(iOS 11.0, *)) {self.session = [[NFCNDEFReaderSession alloc]initWithDelegate:self queue:nil invalidateAfterFirstRead:NO];self.session.alertMessage = @"准备扫描,请将卡片贴近手机";[self.session beginSession];}
}
//获取类型名数组
+(NSArray*)getNameFormatArray{return @[@"Empty",@"NFCWellKnown",@"Media",@"AbsoluteURI",@"NFCExternal",@"Unknown",@"Unchanged"];
}
//获取类型名字
+(NSString*)getNameFormat:(NFCTypeNameFormat)typeName{if (typeName==NFCTypeNameFormatEmpty) {return @"Empty";}else if (typeName==NFCTypeNameFormatNFCWellKnown){return @"NFCWellKnown";}else if (typeName==NFCTypeNameFormatMedia){return @"Media";}else if (typeName==NFCTypeNameFormatAbsoluteURI){return @"AbsoluteURI";}else if (typeName==NFCTypeNameFormatNFCExternal){return @"NFCExternal";}else if (typeName==NFCTypeNameFormatUnknown){return @"Unknown";}else if (typeName==NFCTypeNameFormatUnchanged){return @"Unchanged";}return @"";
}
//获取类型结构体
+(NFCTypeNameFormat)getNFCTypeNameFormat:(NSString*)typeName{if ([typeName isEqualToString:@"Empty"]) {return NFCTypeNameFormatEmpty;}else if ([typeName isEqualToString:@"NFCWellKnown"]){return NFCTypeNameFormatNFCWellKnown;}else if ([typeName isEqualToString:@"Media"]){return NFCTypeNameFormatMedia;}else if ([typeName isEqualToString:@"AbsoluteURI"]){return NFCTypeNameFormatAbsoluteURI;}else if ([typeName isEqualToString:@"NFCExternal"]){return NFCTypeNameFormatNFCExternal;}else if ([typeName isEqualToString:@"Unknown"]){return NFCTypeNameFormatUnknown;}else if ([typeName isEqualToString:@"Unchanged"]){return NFCTypeNameFormatUnchanged;}return NFCTypeNameFormatEmpty;
}
-(NFCNDEFMessage*)createAMessage{NSString* type = @"U";NSData* typeData = [type dataUsingEncoding:NSUTF8StringEncoding];NSString* identifier = @"12345678";NSData* identifierData = [identifier dataUsingEncoding:NSUTF8StringEncoding];NSString* payload1 = @"ahttps://www.baidu.com";NSData* payloadData1 = [payload1 dataUsingEncoding:NSUTF8StringEncoding];if (@available(iOS 13.0, *)) {NFCNDEFPayload *NDEFPayload1=[[NFCNDEFPayload alloc]initWithFormat:NFCTypeNameFormatNFCWellKnown type:typeData identifier:identifierData payload:payloadData1];NFCNDEFMessage* message = [[NFCNDEFMessage alloc]initWithNDEFRecords:@[NDEFPayload1]];return message;} else {return nil;}
}
//停止扫描
-(void)invalidateSession{if (!self.moreTag) {[self.session invalidateSession];}
}
#pragma mark - NFCNDEFReaderSessionDelegate
//读取失败回调-读取成功后还是会回调这个方法
- (void)readerSession:(NFCNDEFReaderSession *)session didInvalidateWithError:(NSError *)error API_AVAILABLE(ios(11.0)){NSLog(@"%@",error);if (error.code == 201) {NSLog(@"扫描超时");}if (error.code == 200) {NSLog(@"取消扫描");}
}
//读取成功回调iOS11-iOS12
- (void)readerSession:(NFCNDEFReaderSession *)session didDetectNDEFs:(NSArray*)messages
API_AVAILABLE(ios(11.0)){dispatch_async(dispatch_get_main_queue(), ^{if (self->isReading) {if (@available(iOS 11.0, *)) {for (NFCNDEFMessage *message in messages) {session.alertMessage = @"读取成功";[self invalidateSession];if (self.scanSuccessBlock) {self.scanSuccessBlock(message);}}}}else{//ios11-ios12下没有写入功能返回失败session.alertMessage = @"写入失败";[self invalidateSession];}});
}
//读取成功回调iOS13
- (void)readerSession:(NFCNDEFReaderSession *)session didDetectTags:(NSArray<__kindof id<NFCNDEFTag>> *)tags API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, macos, tvos){dispatch_async(dispatch_get_main_queue(), ^{if (tags.count>1) {session.alertMessage=@"存在多个标签";[session restartPolling];return;}id tag=tags.firstObject;[session connectToTag:tag completionHandler:^(NSError * _Nullable error) {if (error) {session.alertMessage = @"连接NFC标签失败";[self invalidateSession];return;}[tag queryNDEFStatusWithCompletionHandler:^(NFCNDEFStatus status, NSUInteger capacity, NSError * _Nullable error) {if (error) {session.alertMessage = @"查询NFC标签状态失败";[self invalidateSession];return;}if (status == NFCNDEFStatusNotSupported) {session.alertMessage = @"标签不是NDEF格式";[self invalidateSession];return;}if (self->isReading) {//读[tag readNDEFWithCompletionHandler:^(NFCNDEFMessage * _Nullable message, NSError * _Nullable error) {if (error) {session.alertMessage = @"读取NFC标签失败";[self invalidateSession];}else if (message==nil) {session.alertMessage = @"NFC标签为空";[self invalidateSession];return;}else {session.alertMessage = @"读取成功";[self invalidateSession];if (self.scanSuccessBlock) {self.scanSuccessBlock(message);}}}];}else{//写数据[tag writeNDEF:self.message completionHandler:^(NSError * _Nullable error) {if (error) {session.alertMessage = @"写入失败";if (self.writErrorBlock) {self.writErrorBlock(error);}}else {session.alertMessage = @"写入成功";if (self.writeSuccessBlock) {self.writeSuccessBlock();}}[self invalidateSession];}];}}];}];});
}
//
- (void)readerSessionDidBecomeActive:(NFCNDEFReaderSession *)session API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, macos, tvos){}
@end