I am able to use the "request system code" command (per FeliCa Card Users Manual, section 4.4.8) to get a list of system codes from the card. This works fine on both single-system cards (eg: ICOCA, nimoca) and multi-system cards (eg: Hayakaken, PASMO, Suica).
I have declared all system codes that are on the cards in my Info.plist:
<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array>
<string>8005</string>
<string>8008</string>
<string>0003</string>
<string>fe00</string>
<string>90b7</string>
<string>927a</string>
<string>86a7</string>
</array>However, the multi-system cards can only read the first system code. When trying to switch system codes by incrementing the upper 4 bits of the IDm (per FeliCa Card Users Manual, section 2.3.4), iOS returns an error:
FelicaReader: DEBUG: System code #1: 0xfe00
FelicaReader: DEBUG: - Requesting service codes for 0xfe00...
FelicaTransceiver: DEBUG: >>> <0c0a1XXXXXXXXXXXXXXX0100>
Sending 11 bytes
2019-09-07 13:10:45.980146+1000 metrodroid[353:20725] [CoreNFC] 00000002 83d0e880 -[NFCTagReaderSession transceive:tagUpdate:error:]:726 Error Domain=NFCError Code=2 "Missing required entitlement" UserInfo={NSLocalizedDescription=Missing required entitlement}
FelicaTransceiver: DEBUG: <!< Error Domain=NFCError Code=2 "Missing required entitlement" UserInfo={NSLocalizedDescription=Missing required entitlement}
NFC Session end Error Domain=NFCError Code=200 "Session invalidated by user"Looking at Console.app, I see this error:
nfcd 00000001 0421f0c0 -[_NFReaderSession _validateFelicaCommand:]:2102
Invalid IDM, packet: {length = 12, bytes = 0x0c0a1XXXXXXXXXXXXXXX0100}I suspect that NFReaderSession._validateFelicaCommand is asserting that the IDm entirely matches the tag it discovered -- but instead it should be checking that everything except the first 4 bits of the IDm match.
This same FeliCa reader code works fine with Android and PC/SC. I see the same behaviour with iOS 13.0 Beta and 13.1 Beta on iPhone 7.
Note: I have redacted everything but the upper 4 bits of the IDm from commands for privacy reasons.