iOS NFC开发(读标签与写标签)

一.说明

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 
#import 

NS_ASSUME_NONNULL_BEGIN

typedef 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;
@end

NS_ASSUME_NONNULL_END

1.NFCManager.m

#import "NFCManager.h"

API_AVAILABLE(ios(11.0))
@interface NFCManager (){
    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> *)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

你可能感兴趣的:(iOS NFC开发(读标签与写标签))