【iOS13】Core NFC で 交通系ICカードを読み取る

iOS13で、CoreNFC を使って、交通系ICカードの「ICOCA」を読み取ってみました。

import UIKit
import CoreNFC
class ViewController: UIViewController, NFCTagReaderSessionDelegate {
var session: NFCTagReaderSession?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func beginScanning(_ sender: UIButton) {
guard NFCTagReaderSession.readingAvailable else {
let alertController = UIAlertController(
title: "Scanning Not Supported",
message: "This device doesn't support tag scanning.",
preferredStyle: .alert
)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
return
}
self.session = NFCTagReaderSession(pollingOption: .iso18092, delegate: self)
self.session?.alertMessage = "Hold your iPhone near the item to learn more about it."
self.session?.begin()
}
func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
print("tagReaderSessionDidBecomeActive(_:)")
}
func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
if let readerError = error as? NFCReaderError {
if (readerError.code != .readerSessionInvalidationErrorFirstNDEFTagRead)
&& (readerError.code != .readerSessionInvalidationErrorUserCanceled) {
let alertController = UIAlertController(
title: "Session Invalidated",
message: error.localizedDescription,
preferredStyle: .alert
)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
DispatchQueue.main.async {
self.present(alertController, animated: true, completion: nil)
}
}
}
self.session = nil
}
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
guard let tag = tags.first, case let .feliCa(felicaTag) = tag else { return }    // ①
session.connect(to: tag) { error in    // ②
if let error = error {
print("Error: ", error)
return
}
let historyServiceCode = Data([0x09, 0x0f].reversed())    // ③
felicaTag.requestService(nodeCodeList: [historyServiceCode]) { nodes, error in    // ④
if let error = error {
print("Error: ", error)
return
}
guard let data = nodes.first, data != Data([0xff, 0xff]) else {    // ⑤
print("サービスが存在しない")
return
}
let blockList = (0..<12).map { Data([0x80, UInt8($0)]) }    // ⑥
felicaTag.readWithoutEncryption(serviceCodeList: [historyServiceCode], blockList: blockList)    // ⑦
{ status1, status2, dataList, error in
if let error = error {
print("Error: ", error)
return
}
guard status1 == 0x00, status2 == 0x00 else {    // ⑧
print("ステータスフラグエラー: ", status1, " / ", status2)
return
}
session.invalidate()    // ⑨
// ここでデータ(dataList)の解析処理
dataList.forEach { data in
print("年: ", Int(data[4] >> 1) + 2000)
print("月: ", ((data[4] & 1) == 1 ? 8 : 0) + Int(data[5] >> 5))
print("日: ", Int(data[5] & 0x1f))
print("入場駅コード: ", data[6...7].map { String(format: "%02x", $0) }.joined())
print("出場駅コード: ", data[8...9].map { String(format: "%02x", $0) }.joined())
print("入場地域コード: ", String(Int(data[15] >> 6), radix: 16))
print("出場地域コード: ", String(Int((data[15] & 0x30) >> 4), radix: 16))
print("残高: ", Int(data[10]) + Int(data[11]) << 8)
}
}
}
}
}
}
年:  2012
月:  7
日:  7
入場駅コード:  ???? (←16進数の数値が入ります)
出場駅コード:  ????(←16進数の数値が入ります)
入場地域コード:  0
出場地域コード:  0
残高:  20
・・・

【参考サイト】

> iOSでSuicaの履歴を読み取る - Qiita

>【WWDC19】Core NFC で FeliCa(Suica) を読み取るサンプル【iOS 13 以降】 - Qiita

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA