💳
RxSwiftでCoreNFCを扱う
CoreNFCのコールバックが辛い
NFCを読み取っていろいろやりたい時にコールバック地獄が辛い
ネストが深くなってめちゃくちゃ見にくくなる
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
let tag = tags.first!
session.connect(
to: tag
) { (error) in
if let _ = error {
session.invalidate(errorMessage: "NFCタグの読み取りに失敗しました。")
return
}
guard case .feliCa(let feliCaTag) = tag else {
session.restartPolling()
return
}
feliCaTag.requestService(
nodeCodeList: self.SERVICE_CODE_LIST
) { nodeKeyVersionList, error in
if let _ = error {
session.invalidate(errorMessage: "NFCタグの読み取りに失敗しました。")
return
}
feliCaTag.readWithoutEncryption(
serviceCodeList: self.SERVICE_CODE_LIST,
blockList: self.BLOCK_LIST
) { status1, status2, dataList, error in
session.invalidate()
// do something.
}
}
}
}
地獄から逃れる為のライブラリ作りました。
読み取り、コマンド実行に対応しているタグは下記の3種です
・FeliCa, MiFare, ISO7816
NFCISO15693及びNFCNDEFReaderSession/NFCVASReaderSessionは未実装のため使用できせん
RxCoreNFCでコールバック地獄を解決
NFCTagReaderSessionをラップしたクラスを実装し、Reactiveに扱えるように
Reactiveにする事で多段ネストの改善、タグのフィルターを完結に行えるように
let sessions = NFCTagReaderSession.rx.open(pollingOption: [.iso18092])
let tags = sessions.begin().tags().felicaTags()
let connectedTags = Observable.combineLatest(tags, sessions)
.flatMap { tag, session in session.connect(tag) }
.share()
let invalidates = connectedTags
.requestService(nodeCodeList: SERVICE_CODE_LIST)
.withLatestFrom(connectedTags)
.readWithoutEncryption(serviceCodeList: SERVICE_CODE_LIST, blockList: BLOCK_LIST })
.do(onNext: { result in
// Do something
})
.withLatestFrom(sessions)
.invalidate()
.first()
button.rx.tap
.flatMapFirst { invalidates }
.subscribe()
.disposed(by: disposeBag)
RxCoreNFCの扱い方
セッション生成
let sessions = NFCTagReaderSession.rx.open(pollingOption: [.iso18092])
タグ読み取りの開始
let sessions = NFCTagReaderSession.rx.open(pollingOption: [.iso18092])
let invalidates = sessions.invalidate()
button.rx.tap
.flatMapFirst { invalidates }
.subscribe()
.disposed(by: disposeBag)
読み取ったタグの取得
let sessions = NFCTagReaderSession.rx.open(pollingOption: [.iso18092])
let invalidates = sessions
.begin()
.tags()
.do(onNext: { tag in
print(tag.isAvailable)
})
.withLatestFrom(sessions)
.invalidate()
button.rx.tap
.flatMapFirst { invalidates }
.subscribe()
.disposed(by: disposeBag)
タグを種別毎にフィルタリング
let sessions = NFCTagReaderSession.rx.open(pollingOption: [.iso18092])
let tags = sessions
.begin()
.tags()
let feliCaTags = tags
.feliCaTags()
.do(onNext: { tag in
print(tag.currentIDm)
})
.map { _ in }
let miFareTags = tags
.miFareTags()
.do(onNext: { tag in
print(tag.identifier)
})
.map { _ in }
let invalidates = feliCaTags.amb(miFareTags)
.withLatestFrom(sessions)
.invalidate()
button.rx.tap
.flatMapFirst { invalidates }
.subscribe()
.disposed(by: disposeBag)
Discussion