スタンプカードのアプリを作っていたとき、QR コードではなく物理的なタグにかざすだけで来店を記録したい、という相談を受けました。カメラを起動してコードを合わせる動作は、店頭の数秒では意外と煩わしい。NFC タグなら、かざすだけで終わります。
Rork Max は Swift アプリを生成してくれますが、Core NFC のように Apple の許可(Entitlement)が絡む機能は、生成コードだけでは動きません。署名まわりと plist の設定を手で整えないと、ビルドは通っても実機でセッションが即座に閉じます。ここでは私自身が個人開発で組み込んだ順序で、つまずいた箇所まで含めて書きます。
まず Entitlement と Info.plist を整える
コードより先に設定です。ここが抜けていると、後のコードがどれだけ正しくても動きません。必要なのは次の3点です。
- Apple Developer のアプリ ID で Near Field Communication Tag Reading の機能を有効化する。
- プロジェクトに
.entitlementsを追加し、com.apple.developer.nfc.readersession.formatsにNDEFを入れる。 - Info.plist に
NFCReaderUsageDescriptionを入れ、なぜ NFC を使うのかを日本語で書く。
3 番目の説明文は審査で見られます。「タグを読み取ります」だけだと曖昧で、リジェクトの種になります。私は「来店ポイントを記録するため、レジ横の NFC タグを読み取ります」のように、利用場面が分かる文面にしています。
<!-- App.entitlements -->
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>NDEF</string>
</array>NDEF 読み取りセッションを書く
設定が済んだら、読み取り本体です。NFCNDEFReaderSession を持つ小さなリーダークラスを作り、Rork Max が生成した画面から呼び出せるようにします。
import CoreNFC
final class TagReader: NSObject, ObservableObject, NFCNDEFReaderSessionDelegate {
@Published var lastPayload: String?
private var session: NFCNDEFReaderSession?
func begin() {
guard NFCNDEFReaderSession.readingAvailable else {
print("NFC not available on this device")
return
}
session = NFCNDEFReaderSession(
delegate: self,
queue: nil,
invalidateAfterFirstRead: true
)
session?.alertMessage = "タグをiPhoneの上部に近づけてください"
session?.begin()
}
func readerSession(_ session: NFCNDEFReaderSession,
didDetectNDEFs messages: [NFCNDEFMessage]) {
guard let record = messages.first?.records.first else { return }
let text = decodeText(from: record) ?? "(empty)"
DispatchQueue.main.async { self.lastPayload = text }
}
func readerSession(_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error) {
// ユーザーキャンセルや読み取り失敗もここに来る
print("session invalidated: \(error.localizedDescription)")
}
}invalidateAfterFirstRead: true にすると、1枚読んだ時点でセッションが閉じます。連続でタグを読みたい場合は false にして、自分で invalidate() を呼ぶ設計にします。スタンプカードのように一度に1枚なら true が素直です。