【发布时间】:2022-06-21 15:26:40
【问题描述】:
上下文
我正在开发一个数字棋盘游戏(带有移动应用程序)。玩家可以通过扫描他们的徽章(包含 UUID)与应用程序交互。
我的问题
我创建了一个 NFCReader 服务来从 NFC 徽章读取数据。当我从该服务调用 scan() 方法时,NFC 弹出窗口也会显示,并且当我将 NFC bagde 放在手机上时它会按预期消失。但是,当我在触发 NFC 检测之前使用 NFC 徽章已经在我的手机上发现重复此操作时,NFC 弹出窗口会按预期显示,但 有时,读取数据后不会关闭.
Bug 情况(在调用扫描方法之前,我的徽章已经在我的手机上被发现:
NFCReader 服务
class NFCReader: NSObject, NFCNDEFReaderSessionDelegate, ObservableObject {
static var instance = NFCReader()
var session: NFCNDEFReaderSession?
var onRecordCallBack: ((_ badgeId: String) -> Void)?
func scan(message: String, onRecord: @escaping (_ badgeId: String) -> Void) {
guard NFCReaderSession.readingAvailable else {
return
}
self.onRecordCallBack = onRecord
self.session = NFCNDEFReaderSession(delegate: self, queue: .main, invalidateAfterFirstRead: false)
self.session?.alertMessage = message
self.session?.begin()
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
if let readerError = error as? NFCReaderError {
print("Error NFC reader : \(readerError.localizedDescription)")
}
self.session = nil
}
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
guard
let message = messages.first,
let record = message.records.first,
let badgeId = String(data: record.payload, encoding: .utf8) else {
return
}
if let onRecordCallBack = self.onRecordCallBack {
onRecordCallBack(badgeId)
}
self.session?.invalidate()
}
}
ViewModel 使用 NFCReader 时
struct InGameTravelView: View {
@StateObject var viewModel = InGameTravelViewModel()
var body: some View {
VStack {
if viewModel.isScanning {
Text("Scan your badge")
.onAppear() {
self.viewModel.scan()
}.onTapGesture {
self.viewModel.scan()
}
ForEach(viewModel.pilots) { pilot in
Text("\(pilot.player.name) \(pilot.player.role.name)")
.onTapGesture {
viewModel.currentPilot = pilot
viewModel.isScanning = false
}
}
} else {
switch viewModel.currentPilot.player.role.slug {
case .GOOD :
GoodPilotActionView(pilot: $viewModel.currentPilot) { selectedMatePilot in
viewModel.onActionFinished(selectedMatePilot)
}
case .BAD :
BadPilotActionView(pilot: $viewModel.currentPilot) {
//
}
}
}
}.onAppear() {
viewModel.fetchData()
}
}
}
extension InGameTravelView {
class InGameTravelViewModel: ObservableObject {
@Published var pilots: [Pilot] = []
@Published var pilotsAlreadyPlayed: [Pilot] = []
@Published var isScanning = true
@Published var currentPilot = Pilot.fake()
func fetchData() {
/* Retrieve current round */
let round = GameManager.instance.state.currentRound
/* Retrieve pilots */
self.pilots = round.pilots
if let pilot = self.pilots.first {
self.currentPilot = pilot
}
}
func scan() {
NFCReader.instance.scan(message: "Scan your badge") { badgeId in
print("badgeId : \(badgeId)")
let round = GameManager.instance.state.currentRound
guard let pilot = round.getPilotByBadgeId(badgeId: badgeId) else {
return
}
self.currentPilot = pilot
self.isScanning = false
}
}
}
如您所见,我手动使 NFC 会话 (self.session?.invalidate()) 无效,以尽快隐藏弹出窗口(以避免玩家自己动手)。
我想强调这个错误是随机发生的。有时,即使徽章在触发扫描之前被弄脏,它也会按预期工作 (GIF 1)。
【问题讨论】: