• Native 태그에 대한 지원 추가

    • NDEF 포맷이 아닌 NFC 태그와의 통신이 가능해진다.
    • 여권, 스마트 카드 연락처, NFC가 활성화된 하드웨어
  • 지원 범위는 기존 Core NFC와 동일(아이폰 7, 7플러스 부터)

  • API 변화

    • 기본적인 인터페이스는 여전히 동일(세션 기반, 태그 읽기 UI, 60초 세션 시간 제한)
    • NDEF 태그 writing, 네이티브 태그 접근 기능 추가(단, 백그라운드 스캐닝은 여전히 NDEF만 된다.)
    • 새로운 세션 타입 추가 - NFCTagReaderSession
      • ISO14443, ISO15693, ISO18092 태그 지원
      • 프로토콜로 구분된 태그 개체(NFCTag)가 콜백으로 날아온다.
      • 새로운 태그를 발견해야 되는경우, 폴링을 재시작하는 기능도 추가된다.
    • 기존 NFCNDEFReaderSession도 태그 쓰기를 지원하도록 인터페이스 변경
      • 콜백도 NFCTagReaderSession 과 동일한 인터페이스로 변경.
    • 사용하기
      • entitlement에 NDEF 뿐 아니라, 네이티브 포맷을 지원하기 위한 엔트리도 추가

        스크린샷 2022-06-04 오후 9.51.12.png

      • 어떤 태그를 지원할지를 정해서, ReaderSession을 만든다.

      • 콜백에서 발견한 태그 개체를 받는다.

      • 원하는 태그 개체와 연결

      • 태그 개체를 통해서 작업 수행

      • 끝나면 세션을 무효화시키고 태그 개체를 릴리즈함

  • NDEF 태그 쓰기

    • 백그라운드 스캐닝이 가능한 NDEF 태그를 iOS에서 만들어 낼 수 있다.

      // 태그 discovery 콜백
      optional func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag])
      
      // NDEF Tag 프로토콜
      var isAvailable: Bool { get }
      func queryNDEFStatus(completionHandler: @escaping (NFCNDEFStatus, Int, Error?) -> Void)
      func readNDEF(completionHandler: @escaping (NFCNDEFMessage?, Error?) -> Void)
      func writeNDEF(_ ndefMessage: NFCNDEFMessage, completionHandler: @escaping (Error?) -> Void)
      func writeLock(completionHandler: @escaping (Error?) -> Void)
      
  • 쓰기 예시

    • delegate와 함께 세션 만들기

      @IBAction func beginScanning(_ sender: Any) {
      	session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false)
      	session?.alertMessage = "Hold your iPhone near the item to learn more about it."
      	session?.begin()
      }
      
    • 콜백 메소드 구현

      func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
      	// 찾아낸 태그와 연결하고, 쓰기 연산을 수행
      	// 연결된 태그는 다른 태그를 연결하거나, 폴링을 다시 시작하거나, 세션이 무효화될 때까지 유지된다.
      	let tag = tags.first!
      	session.connect(to: tag) { (error: Error?) in
      		tag.queryNDEFStatus() { (ndefStatus: NFCNDEFStatus, capacity: Int, error: Error?) in 
      		guard ndefStatus == .readWrite else { return } // 쓰기를 지원해야 함
      
      		let myMessage = NFCNDEFMessage(data: Data()) // capacity를 넘어가는 메시지는 쓰면 안됨
      
      		tag.writeNDEF(myMessage) { (error: Error?) in
      			session.invalidate()
      		}
      	}
      }
      
  • 네이티브 태그 읽기

    • ISO7816 - Type A와 Type B

      • 사용 예시

        • 전자 ID(전자 여권 등)
        • 스마트 카드 연락처
        • 결제
        • 교통
      • APDU(Application Protocol Data Unit)인터페이스를 활용함

      • 요구사항

        • Application Identifier(AID) 리스트가 Info.plist에 있어야 함

          스크린샷 2022-06-04 오후 10.51.14.png

          • AID 자체가 아무값이나 쓰면 되는 게 아니고 국제 표준으로 관리됨
        • 태그 발견 콜백은 태그가 ISO7816에 호환되고, 명시된 AID를 포함하고 있어야만 호출됨

        • 이때 AID가 미리 선택된 상태로 전달되긴 하지만, 일단 전달되고 나면 다른 AID를 선택해도 됨

        • 결제용 카드는 지원하지 않는다. (결제 관련 AID는 미지원)

      • NFCISO7816Tag

        • 프로퍼티

          • identifier(UID)
          • historicalBytes
          • applicationData
        • 메소드

          func sendCommand(apdu: NFCIOS7816APDU, completionHandler: @escaping (Data, UInt8, UInt8, Error?) -> Void)
          
      • 구현 예시

        • session 만들기

          @IBAction func beginScanning(_ sender: Any) {
          	session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self) // iso14443: Type A,B의 기반 기술
          	session?.alertMessage = "Hold your iPhone near the ISO7816 tag to begin transaction"
          	session?.begin()
          }
          
        • 태그 연결 및 작업

          func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
          	if case let NFCTag.iso7816(tag) = tags.first {
          		session.connect(to: tag) { (error: Error?) in
          			let myAPDU = NFCISO7816APDU(instructionClass: 0, instructionCode: 0x00, p1Parameter: 0,
          										  p2parameter: 0, data: Data(), expectedResponseLength: 16)
          			tag.sendCommand(apdu: myAPDU) { (response: Data, sw1: UInt8, sw2: UInt8, error: Error?) in
          				guard error != nil && !(sw1 == 0x90 && sw2 == 0) else {
          					session.invalidate(errorMessage: "Application failure") // sheet ui에 에러메시지가 나오게 된다.
          					return
          				}
          			}
          		}
          	}
          }
          
    • MiFare

      • Type A 태그 기반의 기술(일부만 호환)

      • NXP에 의해 개발되었고, 티케팅과 뱃지 시스템에서 전세계적으로 사용됨

      • NFCMiFareTag

        • 프로퍼티

          • identifier(UID)
          • historicalBytes
          • miFareFamily - Ultralight, Plust, DESFire. (MiFare 클래식은 미지원)
        • 메소드

          func sendMiFareCommand(commandPacket command: Data, completionHandler: @escaping (Data, Error?) -> Void)
          func sendMiFareISO7816Command(apdu: NFCIOS7816APDU, completionHandler: @escaping (Data, UInt8, UInt8, Error?) -> Void)
          
      • 구현 예시

        • session 만들기

          • MiFare 태그여도, ISO7816을 지원하고, 앱이 지원하는 AID가 포함된 경우는 MiFare가 아닌 ISO7816으로 들어온다.
          @IBAction func beginScanning(_ sender: Any) {
          	session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self) // MiFare도 iso14443으로 지정
          	session?.alertMessage = "Hold your iPhone near the tag to begin transaction"
          	session?.begin()
          }
          
        • 태그 연결 및 작업

          func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
          	if case let NFCTag.miFare(tag) = tags.first {
          		session.connect(to: tag) { (error: Error?) in
          			// ...
          			tag.sendMiFareCommand(coomandPacket: command) { (response: Data, error: Error?) in
          				// 에러 확인 후 결과 처리
          			}
          		}
          	}
          }
          
    • ISO15693 - Type 5 혹은 vicinity tag(주변 태그, 도난 방지용으로 제품에 붙어있는 태그 생각하면 됨)

      • 상품 판매, 산업용, 의료용으로도 사용됨

      • NFCISO15693Tag

        • 프로퍼티
          • identifier(UID)
          • icManufacturereCode
          • icSerialNumber
        • 커맨드가 방대하기 때문에 단일 메소드로는 제공되지 않는다.
          • 대신 일반적인 연산들에 대해서는 편의 메소드를 제공한다.

            스크린샷 2022-06-04 오후 11.43.36.png

      • 구현 예시

        • session 만들기

          @IBAction func beginScanning(_ sender: Any) {
          	session = NFCTagReaderSession(pollingOption: .iso15693, delegate: self) // MiFare도 iso14443으로 지정
          	session?.alertMessage = "Hold your iPhone near the tag to begin transaction"
          	session?.begin()
          }
          
        • 태그 연결 및 작업

          func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
          	if case let NFCTag.iso15693(tag) = tags.first {
          		session.connect(to: tag) { (error: Error?) in
          			// ...
          			tag.readSingleBlock(requestFlags: [.highDataRate, .address], blockNumber: 0) { (response: Data, error: Error?) in
          				// 에러 확인 후 결과 처리
          			}
          		}
          	}
          }
          
    • FeliCa

  • 데모