美文网首页APP Extension扩展功能NFCiOS常用
iOS NFC开发(读标签与写标签)

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

作者: 宋魁鹏 | 来源:发表于2019-12-30 16:05 被阅读0次

    一.说明

    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_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 ()<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
    
    

    相关文章

      网友评论

        本文标题:iOS NFC开发(读标签与写标签)

        本文链接:https://www.haomeiwen.com/subject/ptsmoctx.html