iOS 11 ——遲來的 NFC

CNKCQ發表於2019-03-04

前言

NFC 這個詞已經並不陌生了,前一段時間北京地鐵支援 NFC 支付一時成為霸佔頭條的熱點。其實在 90 年代末到 2000 年初,二維碼和 NFC 就已經相繼誕生,由於二維碼成本低廉,技術門檻相對較低,因此,二維碼迅速搶佔了移動支付的市場,但 NFC 的發展並未因此停止。在 Android 端的 NFC 發展已經非常迅猛了,可惜 Apple 遲遲未開放介面,在今年的 WWDC 上,蘋果宣佈開放其 NFC 介面 CoreNFC, 這為以後 NFC 的應用提供了更多的可能。

正文

如果你對 NFC 這項技術還比較陌生,那麼這裡科普一下,NFC(Near Field Communication)近場通訊,當兩個裝置相互靠近時能進行資訊交流。許多企業講 NFC 晶片放到卡片裡,用帶有 NFC 晶片的卡片來授予許可權將允許誰有許可權,比如說進出入公司。Apple CoreNFC 目前支援的格式有限,NFC 資料交換格式或 NDEF(通常用於當今市場上的大多數平板電腦和手機),比如 Apple Pay 。

CoreNFC Demo

這裡我們通過一個簡單的例項程式來演示怎麼使用 CoreNFC,這個程式可以用來讀取儲存在卡片上 NDEF 格式的資訊。

working-scanner.png

為此,我使用 Arduino Uno 與 Adafruit PN532 Shield 配對,將其傳送到樣品 NDEF 格式的卡上。 如果你不具備這些東西,或者根本不想在這樣的硬體上投入時間和金錢,請嘗試找一張帶有資訊的預格式化卡。 本文中,我不會演示 NFC 格式化以及如何把資料儲存到 NDEF 卡 中。

Getting Started

開啟 Xcode 9 建立一個簡單的 Swift 工程。使用 Storyboard 建立簡單的頁面:

iOS 11 ——遲來的 NFC

ViewController 如下:

import UIKit
 
class ViewController: UIViewController {
 
    @IBOutlet weak var messageLabel: UILabel!
 
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
 
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
 
    @IBAction func scanPressed(_ sender: Any) {
        // this is our newly created IBAction
    }
 
}
複製程式碼

Entitlements & Privacy

我們的 app 要使用 NFC 必須進行應用授權:前提是你得有一個有效 Apple id (交過保護費的). 進行應用程式授權和隱私設定,開啟developer.apple.com。 登入你的帳戶,建立一個證書 —— 註冊一個新的 APP ID。應用說明應該要支援 NFC 點選下一步,確保你的確認頁面如下圖:

iOS 11 ——遲來的 NFC

然後再建立 provisioning profile :

iOS 11 ——遲來的 NFC

這一步完成了,在我們剛建立的專案中匯入 證書和描述檔案,完成之後呢,我們還需進行 Info.plist 配置 Privacy:

iOS 11 ——遲來的 NFC

至此,我們的開始工作就完成了。?我們進入 coding 階段。

Core NFC

要實現 NFC 功能,我們得接入 Core NFC framework:

import CoreNFC 

複製程式碼

目前為止,iOS模擬器尚未支援 CoreNFC。 這意味著如你嘗試匯入CoreNFC,會收到一條錯誤,表示沒有名為 CoreNFC 的模組。 遇到這種情況,請選擇你的 iPhone 或 Generic iOS Device。接下來我們實現 NFCNDEFReaderSessionDelegate 協議:

import UIKit
import CoreNFC 

class ViewController: UIViewController, NFCNDEFReaderSessionDelegate { 

    @IBOutlet weak var messageLabel: UILabel!
    var nfcSession: NFCNDEFReaderSession?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func scanPressed(_ sender: Any) {

    }

    func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
        print("The session was invalidated: (error.localizedDescription)")
    }

    func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
        // Parse the card`s information
    }
}
複製程式碼

其中兩個 readerSession 函式會分別告訴我們 NFC 會話成功或失敗,成功後則返回 NFCNDEFMessage 格式的通訊資料,失敗後會返回 error 資訊。
  當然,我們首先還需要初始化 NFCNDEFReaderSession 並開啟 NFC 監聽。

@IBAction func scanPressed(_ sender: Any) {
    nfcSession = NFCNDEFReaderSession.init(delegate: self, queue: nil, invalidateAfterFirstRead: true)
    nfcSession?.begin()
}
複製程式碼

然後執行程式,看看:

iOS 11 ——遲來的 NFC

如果提示 Session is invalidated unexpectedly ,那麼請仔細核對 證書、描述檔案以及 Privacy 設定是否正確。
這個過程並不難,簡單幾步就能搞定,下面我們來看看怎麼解析獲取到的 message 。

解析 Message

首先,讓我們來看一下這個函式:

func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) 
複製程式碼

讓我們來看看每一個 message 物件包含了哪些資訊:

print(messages[0])
複製程式碼

我們可以看到:

( // Payload one (There`s only one payload in this card)
    "TNF=1, /* Type Name Format */
    Payload Type=<55>,
    Payload ID=<>,
    Payload=<0048656c 6c6f21>" /* What we`re really interested in */
)
複製程式碼

根據列印的結果我們可以看出:
. messages 是一個 NFCNDEFMessages 物件的陣列。
. NFCNDEFMessage 有一個 NFCNDEFPayload 物件陣列 records
然後我們再來看看每一個 payload 又包含了哪些資訊:

  1. identifier.
  2. type.
  3. typeNameFormat.
  4. payload.

這裡其實我們只關心 payload 。好了,看看如何解析 records 吧。

func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
    var result = ""
    for payload in messages[0].records {
        result += String.init(data: payload.payload.advanced(by: 3), encoding: .utf8)! // 1
    }

    DispatchQueue.main.async {
        self.messageLabel.text = result
    }
}
複製程式碼

來看看最終的效果:

nice result

參考

Working with CoreNFC in iOS 11

相關文章