ライブラリTRET JapanNFCReaderを使って交通系ICカードの残高を読み取ってみました。
https://github.com/treastrain/TRETJapanNFCReader
iOS16,Xode14です。
XcodeのFile、Add Packagesでパッケージを追加します。

右上の検索ボックスに上記のGithubのURLを入れると検索できます。
ダウンロードできるパッケージは選択できますが、大事なものが欠けているとエラーが出ますので、必要そうなものは多めに選択してダウンロードしておきます。

type-BのMIFARE以下のパッケージは、運転免許証やマイナンバー用のようです。
次にCapabilitiesでNFCを有効にします。

次にinfo.plistを以下のようにします。

item0の「0003」はFelicaを読み取る際のパラメータです。
以上で準備ができました。
コードを書いていきます。
import Foundation
import TRETJapanNFCReader
import TRETJapanNFCReader_FeliCa
import TRETJapanNFCReader_FeliCa_TransitIC
final class ViewModel: NSObject, ObservableObject, FeliCaReaderSessionDelegate {
@Published var balance: Int? = nil
@Published var transactions:[Data]? = nil
var reader: TransitICReader!
//読み取りを開始する
func readingStart(){
self.reader = TransitICReader(delegate: self)
self.reader.get(itemTypes: [.balance])
}
func japanNFCReaderSession(didInvalidateWithError error: Error) {
print(error)
}
//Felicaの読み取りが終わった後に、残高を格納する
func feliCaReaderSession(didRead feliCaCardData: TRETJapanNFCReader_FeliCa.FeliCaCardData, pollingErrors: [TRETJapanNFCReader_FeliCa.FeliCaSystemCode : Error?]?, readErrors: [TRETJapanNFCReader_FeliCa.FeliCaSystemCode : [TRETJapanNFCReader_FeliCa.FeliCaServiceCode : Error]]?) {
let transitICCardData = feliCaCardData as! TransitICCardData
DispatchQueue.main.async {
self.balance = transitICCardData.balance
}
}
func feliCaReaderSession(didInvalidateWithError pollingErrors: [TRETJapanNFCReader_FeliCa.FeliCaSystemCode : Error?]?, readErrors: [TRETJapanNFCReader_FeliCa.FeliCaSystemCode : [TRETJapanNFCReader_FeliCa.FeliCaServiceCode : Error]]?) {
}
}
Felicaの読み取りが完了するとdidReadが呼ばれるので、カードデータから残高を代入しています。
UIは以下の通り。
struct ContentView: View {
@ObservedObject var vm: ViewModel
var body: some View {
VStack {
Text("交通系ICカードの残高")
.font(.title)
if (self.vm.balance != nil) {
Text("¥\(String(self.vm.balance!))")
.font(.largeTitle)
.fontWeight(.bold)
}
Button {
vm.readingStart()
} label: {
Text("ICカードの読み取りを開始する")
.font(.title)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let vm = ViewModel()
vm.balance = 2000
return ContentView(vm: vm)
}
}
テキストとボタンのみのUIです。
アップルウォッチのモバイルSuicaに対して読み取りができました。
残高の他に入退出履歴も表示させる場合には、Data型をゴリゴリ解析する必要がありそうです。