iOS开发之NFC的使用

一、了解NFC
iOS开发关于NFC的使用,其实官方文档已经很详细了,先上官方文档:
文档地址:https://developer.apple.com/documentation/corenfc?language=objc
支持NFC的设备:
1、iOS11上的手机只支持读的功能,可以通过手机的NFC功能读标签.
2、iOS13以上的系统支持写的功能。
3、支持NFC的最低设备为iPhone 7和iPhone 7Plus

二、读取NFC标签NDEF
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中的数据格式。
详情可以参考:https://blog.csdn.net/vampire_armand/article/details/39372953
1、导入头文件
object-c

#import <CoreNFC/CoreNFC.h>

swift

import CoreNFC

2、读取NDEF格式的NFC
2.1本地配置
参考官方文档:https://developer.apple.com/documentation/corenfc/adding_support_for_background_tag_reading
2.1.1打开读取NFC标签权限,Xcode打开权限后会自动同步到开发者的Appid中

image.png
image.png

同时生成.entitlements文件


image.png

2.1.2 添加权限描述
Privacy - NFC Scan Usage Description
String


image.png

官方demo地址
[Building an NFC Tag-Reader App] Read NFC tags with NDEF messages in your app.
https://developer.apple.com/documentation/corenfc/building_an_nfc_tag-reader_app?language=objc(用于扫描NDEF格式的NFC,iOS11-iOS12只读不能写,iOS13以后可以读可写)
代码

#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);
API_AVAILABLE(ios(13.0))
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;

+(NFCManager *)sharedInstance;

//判断是否支持读写功能
+(NFCSupportsStatus)isSupportsNFCReading;
-(void)scanTagWithSuccessBlock:(NFCScanSuccessBlock)scanSuccessBlock andErrorBlock:(NFCScanErrorBlock)scanErrorBlock;

@end

NS_ASSUME_NONNULL_END
#import "NFCManager.h"


@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;
}

+(NFCSupportsStatus)isSupportsNFCReading{
    if (@available(iOS 11.0,*)) {
        if (NFCNDEFReaderSession.readingAvailable == YES) {
            return NFCSupportStatusYes;
        }
        else{
            NSLog(@"%@",@"该机型不支持NFC功能!");
            return NFCSupportStatusDeviceNo;
        }
    }
    else {
        NSLog(@"%@",@"当前系统不支持NFC功能!");
        return NFCSupportStatusnSystemNo;
    }
}


-(void)scanTagWithSuccessBlock:(NFCScanSuccessBlock)scanSuccessBlock andErrorBlock:(NFCScanErrorBlock)scanErrorBlock{
    self.scanSuccessBlock=scanSuccessBlock;
    self.scanErrorBlock=scanErrorBlock;
    isReading=YES;
    [self beginScan];
}


-(void)beginScan{
    if (@available(iOS 11.0, *)) {
        self.session = [[NFCNDEFReaderSession alloc]initWithDelegate:self queue:nil invalidateAfterFirstRead:NO];
        self.session.alertMessage = @"准备扫描,请将卡片贴近手机";
        [self.session beginSession];
    }
}

//停止扫描
-(void)invalidateSession{
        [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

3、读取带标签的NFC
[Creating NFC Tags from Your iPhone] Save data to tags, and interact with them using native tag protocols.
https://developer.apple.com/documentation/corenfc/creating_nfc_tags_from_your_iphone(可以扫描iso15693、iso7816、felica、miFare等NFC,可以读、写,但是必须有Aid)
3.1 需要在info.plist中添加对应的标签,标签为数组,添加所适配NFC的Aid

image.png

注意:swift项目中,如果不使用某标签,info.plist就不要添加,初始化的时候也不要带该标签,如果info.plist添加了该标签,一定要加Aid,否则会导致无法唤起NFC扫描的页面。
3.2代码
object-c
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;

@end

NS_ASSUME_NONNULL_END

NFCManager.m

#import "NFCManager.h"

@interface NFCManager ()<NFCTagReaderSessionDelegate>{
    BOOL isReading;
}

@property (strong, nonatomic) NFCTagReaderSession *tagReaderSession;

@property (strong, nonatomic) NFCNDEFMessage *message;

@property(nonatomic,weak)id<NFCISO7816Tag> currentTag;

@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.tagReaderSession = [[NFCTagReaderSession alloc]initWithPollingOption:NFCPollingISO14443 delegate:self queue:dispatch_queue_create("beckhams",DISPATCH_QUEUE_SERIAL)];
        self.tagReaderSession.alertMessage = @"message";
            [self.tagReaderSession beginSession];
        
    }
}


-(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{
    [self.tagReaderSession invalidateSession];
}




- (void)tagReaderSession:(NFCTagReaderSession *)session didDetectTags:(NSArray<__kindof id<NFCTag>> *)tags API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, macos, tvos){
    if (tags.count > 1){
        NSLog(@"读卡错误");
        return;
    }
    
    
    id<NFCISO7816Tag> Tag7816 = [tags.firstObject asNFCISO7816Tag];
    //这里的Tag7816实例是用于后面发送指令的对象。
    if (Tag7816 == nil){
        NSLog(@"读取到的非7816卡片");
        return;
    }
    // 这里获取到的AID就是第一步中在info.plist中设置的ID (A000000000)这个值一般是卡商提供的,代表卡的应用表示。
    NSLog(@"Tag7816.initialSelectedAID:%@",Tag7816.initialSelectedAID);
    __weak typeof(self) weakSelf = self;
    [self.tagReaderSession connectToTag:Tag7816 completionHandler:^(NSError * _Nullable error) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (error){
            NSLog(@"TconnectToTag:%@",error);
            return;
        }
        self.currentTag = Tag7816;
        self.tagReaderSession.alertMessage = @"已识别到NFC";
        // 这里就可以开始执行指令和cpu卡交互了。
        [self sendApduSingle:@"00A400000112501"];//16进制指令

        
    }];
}
// 发送指令的示例代码:
-(void)sendApduSingle:(NSString *)apduStr{
    //  apduStr 是发送的指令字符串,比如 00A5030004B000000033434561
    NSData *apduData = [self convertHexStrToData:apduStr]; // 把指令转成data格式
    NFCISO7816APDU *cmd = [[NFCISO7816APDU alloc]initWithData:apduData];  // 初始化 NFCISO7816APDU。
    
    __block NSData *recvData = nil;
    __block NSError *lerror = nil;
    __block BOOL bRecv = NO;
    __block int lsw = 0;
    NSLog(@"send data => %@", apduData);
    
    // 这里的Tag7816就是上面协议中拿到的tag
    [self.currentTag sendCommandAPDU:cmd completionHandler:^(NSData * _Nonnull responseData, uint8_t sw1, uint8_t sw2, NSError * _Nullable error) {
        NSLog(@"------resp:%@ sw:%02x%02x  error:%@", responseData, sw1, sw2, error);
        NSLog(@"responseData十六进制:%@", [self convertApduListDataToHexStr:responseData]);
        lerror = error;
        lsw = sw1;
        lsw = (lsw << 8) | sw2;
        if (responseData) {
            recvData = [[NSData alloc]initWithData:responseData];
        }
        // 拿到返回的数据了,根据具体的业务需求去写代码。。。。
        [self invalidateSession];
    }];
}


//将字符串转NSData
- (NSData *)convertHexStrToData:(NSString *)str {
    if (!str || [str length] == 0) {
        return nil;
    }
    
    NSMutableData *hexData = [[NSMutableData alloc] initWithCapacity:8];
    NSRange range;
    if ([str length] % 2 == 0) {
        range = NSMakeRange(0, 2);
    } else {
        range = NSMakeRange(0, 1);
    }
    for (NSInteger i = range.location; i < [str length]; i += 2) {
        unsigned int anInt;
        NSString *hexCharStr = [str substringWithRange:range];
        NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr];
        
        [scanner scanHexInt:&anInt];
        NSData *entity = [[NSData alloc] initWithBytes:&anInt length:1];
        [hexData appendData:entity];
        
        range.location += range.length;
        range.length = 2;
    }
    return hexData;
}


//NSData转字符串
-(NSString *)convertApduListDataToHexStr:(NSData *)data{
        if (!data || [data length] == 0) {
            return @"";
        }
        NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[data length]];
        [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
            unsigned char *dataBytes = (unsigned char*)bytes;
            for (NSInteger i = 0; i < byteRange.length; i++) {
                NSString *hexStr = [NSString stringWithFormat:@"%x", (dataBytes[i]) & 0xff];
                if ([hexStr length] == 2) {
                        [string appendString:hexStr];
                } else {
                    [string appendFormat:@"0%@", hexStr];
                }
            }
        }];
        return [string uppercaseString];
}

- (void)tagReaderSession:(NFCTagReaderSession *)session didInvalidateWithError:(NSError *)error API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, macos, tvos){
    NSLog(@"%@",error);
}


@end

Swift

import UIKit
import CoreNFC

class SwiftNFCManager: NSObject ,NFCTagReaderSessionDelegate{
    enum NFCSupportsStatus : NSInteger {
        case NFCSupportStatusYes = 0 //支持
        case NFCSupportStatusDeviceNo = 1 //硬件不支持
        case NFCSupportStatusnSystemNo = 2//系统不支持
    }
    typealias  block = (Data,Bool) -> Void
    var completeHandle:block?
    typealias  NFCScanSuccessBlock = (String) -> Void
    typealias  NFCScanErrorBlock = (String) -> Void
    var delegate:NfcProtocol?
    var isReading:Bool?
    var scanSuccessBlock:NFCScanSuccessBlock?
    var scanErrorBlock:NFCScanErrorBlock?
    var tagSession:NFCTagReaderSession?
    var currentTag:NFCISO7816Tag?
    static let shared = SwiftNFCManager()
    
    class func isSupportsNFCReading()->NFCSupportsStatus{
        if #available(iOS 11.0, *){
            if NFCNDEFReaderSession.readingAvailable == true{
                return NFCSupportsStatus.NFCSupportStatusYes
            }else{
                print("该机型不支持NFC功能!")
                return NFCSupportsStatus.NFCSupportStatusDeviceNo
            }
        }else{
            print("当前系统不支持NFC功能!")
            return NFCSupportsStatus.NFCSupportStatusnSystemNo
        }
    }
    class func isSupportsNFCWrite()->NFCSupportsStatus{
        if #available(iOS 13.0, *){
            if NFCNDEFReaderSession.readingAvailable == true{
                return NFCSupportsStatus.NFCSupportStatusYes
            }else{
                print("该机型不支持NFC功能!")
                return NFCSupportsStatus.NFCSupportStatusDeviceNo
            }
        }else{
            print("当前系统不支持NFC写入功能!")
            return NFCSupportsStatus.NFCSupportStatusnSystemNo
        }
    }
    func startScanNFC(successBlock:@escaping NFCScanSuccessBlock,errorBlock:@escaping NFCScanErrorBlock) {
        scanSuccessBlock = successBlock
        scanErrorBlock = errorBlock
        isReading = true
        self.beginScan()
    }
    func beginScan() {
        if #available(iOS 11.0, *){
            tagSession = NFCTagReaderSession(pollingOption: [.iso14443, .iso15693, .iso18092], delegate: self, queue: nil)
            tagSession?.alertMessage = "Hold your iPhone near an NFC Type 2 tag."
               tagSession?.begin()
        }
    }
    //停止扫描
    func invalidateSession() {
        self.tagSession?.invalidate()
    }
    
    
    //MARK: NFCTagReaderSessionDelegate
     
     func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
         print("tagReaderSessionDidBecomeActive")
     }
     
     func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
         scanErrorBlock!("扫描NFC失败")
         print("error:\(error)")
     }
     
     func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
         
         if tags.count > 1 {
             print("读卡错误")
             scanErrorBlock!("读卡错误")
             session.alertMessage = "More than 1 tags was found. Please present only 1 tag."
             return
         }
         
         
         let Tag7816:NFCTag = tags.first!
         switch Tag7816 {
         case .feliCa(_):
             print("NFCFeliCaTag")
         case let .iso7816(tag):
             // 这里获取到的AID就是第一步中在info.plist中设置的ID (A000000000)这个值一般是卡商提供的,代表卡的应用表示。
             self.tagSession?.connect(to: Tag7816, completionHandler: {[weak self] (error) in
                 if error != nil {
                     print("TconnectToTag:\(String(describing: error))")
                     session.invalidate(errorMessage: "Connection error. Please try again.")
                     self?.delegate?.connectNfcError()
                     return
                 }
                 self?.delegate?.connectNfcSuccess()
                 self?.currentTag = tag
                 self?.tagSession?.alertMessage = "已经识别到NFC"
                 self?.invalidateSession()
             })
         case .iso15693(_):
             print("NFCISO15693Tag")
         case .miFare(_):
             print("NFCMiFareTag")
         @unknown default:
             session.invalidate(errorMessage: "Tag not valid.")
             return
         }

     }
     

     
     func sendApduSingle(apduStr:String,comPlete:block){
         let sendData:Data = self.convertHexStrToData(hexStr: apduStr)
         let cmd:NFCISO7816APDU = NFCISO7816APDU.init(data: sendData)!
         var resultError:NSError?
         var result:Data?
         self.currentTag?.sendCommand(apdu: cmd, completionHandler: { [weak self] (resultData, sw1, sw2, error) in
             print("resultData:\(resultData)\nsw1:\(sw1)\nsw2:\(sw2)\nerror:\(String(describing: error))")
             print("rusultData转16进制:\(String(describing: self?.string(from: resultData)))")

             result = resultData
             resultError = error as NSError?
         })
         
         if resultError == nil{
             comPlete(result ?? Data.init(),true)
         }else{
             comPlete(Data.init(),false)
         }
     }
     
     
     
     //将十六进制字符串转化为 Data
     func convertHexStrToData(hexStr:String) -> Data {
         let bytes = self.bytes(from: hexStr)
 //        let bytes = hexStr.bytes(from: hexStr)
         return Data.init(_:bytes)
     }
     
     // 将16进制字符串转化为 [UInt8]
       // 使用的时候直接初始化出 Data
       // Data(bytes: Array<UInt8>)
     func bytes(from hexStr: String) -> [UInt8] {
         assert(hexStr.count % 2 == 0, "输入字符串格式不对,8位代表一个字符")
         var bytes = [UInt8]()
         var sum = 0
         // 整形的 utf8 编码范围
         let intRange = 48...57
         // 小写 a~f 的 utf8 的编码范围
         let lowercaseRange = 97...102
         // 大写 A~F 的 utf8 的编码范围
         let uppercasedRange = 65...70
         for (index, c) in hexStr.utf8CString.enumerated() {
             var intC = Int(c.byteSwapped)
             if intC == 0 {
                 break
             } else if intRange.contains(intC) {
                 intC -= 48
             } else if lowercaseRange.contains(intC) {
                 intC -= 87
             } else if uppercasedRange.contains(intC) {
                 intC -= 55
             } else {
                 assertionFailure("输入字符串格式不对,每个字符都需要在0~9,a~f,A~F内")
             }
             sum = sum * 16 + intC
             // 每两个十六进制字母代表8位,即一个字节
             if index % 2 != 0 {
                 bytes.append(UInt8(sum))
                 sum = 0
             }
         }
         return bytes
     }
     //讲Data转String
     func string(from data: Data) -> String {
             return String(format: "%@", data as CVarArg)
         }

demo

本文主要是手机nfc与ISO7816协议间的交互。例如上海交通卡遵守的是ISO7816协议,可以正常获取上海交通卡的余额等信息。(卡的Aid可以从交通卡ipa包中获取)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • iOS11 Core NFC iPhone6开始支持NFC(Near Field Communication )...
    缭雾阅读 1,895评论 13 3
  • 一、NFC的使用范围 苹果在iOS11上推出了NFC的功能,开发者可以根据自身的需要使用这个功能进行开发。NFC有...
    三国韩信阅读 4,003评论 3 23
  • 一、NFC简介 NFC(Near Field Communication,NFC)近场通信,是一种短距高频的无线电...
    张聪_2048阅读 20,799评论 8 42
  • About Core NFC Core NFC支持的读取数据类型: Core NFC框架特性/要求 目前支持NFC...
    EchoZuo阅读 9,544评论 39 14
  • 先了解几个概念什么是NFC?NDEF指的是什么?什么是CoreNFC? 什么是 NFC NFC(Near Fiel...
    A訫飛Flyme阅读 10,795评论 10 6