Edited at

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

iOS 13 以降の Core NFC では FeliCa の読み取りに対応したことがわかったので、読み取るためのサンプルコードを載せます。


サンプルコード

//  Created by treastrain on 2019/06/06.

// Copyright © 2019 treastrain / Tanaka Ryoga. All rights reserved.
//

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]) {
print("tagReaderSession(_:didDetect:)")

if tags.count > 1 {
let retryInterval = DispatchTimeInterval.milliseconds(500)
session.alertMessage = "More than 1 tag is detected, please remove all tags and try again."
DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: {
session.restartPolling()
})
return
}

let tag = tags.first!

session.connect(to: tag) { (error) in
if nil != error {
session.invalidate(errorMessage: "Connection error. Please try again.")
return
}

guard case .feliCa(let feliCaTag) = tag else {
let retryInterval = DispatchTimeInterval.milliseconds(500)
session.alertMessage = "A tag that is not FeliCa is detected, please try again with tag FeliCa."
DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: {
session.restartPolling()
})
return
}

print(feliCaTag)

let idm = feliCaTag.currentIDm.map { String(format: "%.2hhx", $0) }.joined()
let systemCode = feliCaTag.currentSystemCode.map { String(format: "%.2hhx", $0) }.joined()

print("IDm: \(idm)")
print("System Code: \(systemCode)")

session.alertMessage = "Read success!\nIDm: \(idm)\nSystem Code: \(systemCode)"
session.invalidate()
}
}

}


設定事項

Core NFC を使うにあたって、コードだけでなく Xcode プロジェクトの設定もいくつか必要です。

特に、読み取りたい FeliCa のシステムコードをあらかじめ全て指定しておく必要があります。

こちらの記事を参照してください。


動作確認

FeliCa カードを読み取ることはもちろん、Apple Watch の Suica であっても(エミュレートカード)読み取ることができます。



【追記 2019年6月12日】

Qiita 記事投稿の制約上、YouTube 動画の埋め込みができないためにサムネイル画像を表示しておりますが、本記事引用等の際に画像の利用はご遠慮ください。YouTube 動画の埋め込みについては問題ありません。ご協力をお願いいたします。


サンプルプロジェクト

このコードが使用されているサンプルプロジェクトを GitHub にて公開しております。

treastrain/ios13-felica-reader: Sample project to read FeliCa on iOS 13 and later - GitHub