Core API
The CoreExtendedNFC namespace exposes the high-level flows used by apps.
public static func scan(
pollingTargets: [NFCSessionManager.PollingTarget] = [.all],
message: String? = nil,
manager: NFCSessionManager = NFCSessionManager()
) async throws -> (CardInfo, any NFCTagTransport, NFCSessionManager)
public static func scanAndDump(
pollingTargets: [NFCSessionManager.PollingTarget] = [.all],
message: String? = nil,
manager: NFCSessionManager = NFCSessionManager()
) async throws -> (CardInfo, MemoryDump)
public static func dumpCard(info: CardInfo, transport: any NFCTagTransport) async throws -> MemoryDump
public static func readNDEF(info: CardInfo, transport: any NFCTagTransport) async throws -> NDEFMessage
public static func writeNDEF(_ message: NDEFMessage, info: CardInfo, transport: any NFCTagTransport) async throws
public static func formatNDEF(info: CardInfo, transport: any NFCTagTransport) async throws
public static func refineCardInfo(_ info: CardInfo, transport: any NFCTagTransport) async throws -> CardInfo
public static func readPassport(
mrzKey: String,
dataGroups: [DataGroupId] = [.com, .dg1, .dg2, .sod],
performActiveAuth: Bool = true,
trustAnchorsDER: [Data] = [],
message: String? = nil,
manager: NFCSessionManager = NFCSessionManager(),
onProgress: (@Sendable (String) -> Void)? = nil
) async throws -> PassportModel
public static func readPassport( // over a transport you already hold; no session handling
transport: any NFCTagTransport,
mrzKey: String,
dataGroups: [DataGroupId] = [.com, .dg1, .dg2, .sod],
performActiveAuth: Bool = true,
trustAnchorsDER: [Data] = [],
onProgress: (@Sendable (String) -> Void)? = nil
) async throws -> PassportModel
public static func readTransitBalance(info: CardInfo, transport: any NFCTagTransport) async throws -> TransitBalance
// My Number
static func readMyNumberCard(
items: [MyNumberCardItem] = [.tokenInfo],
cardInfoInputSupportPIN: String? = nil,
message: String? = nil,
manager: NFCSessionManager = NFCSessionManager(),
onProgress: (@Sendable (String) -> Void)? = nil
) async throws -> MyNumberCardData
static func readMyNumberCard(transport: any NFCTagTransport, items: [MyNumberCardItem] = [.tokenInfo], cardInfoInputSupportPIN: String? = nil, onProgress: (@Sendable (String) -> Void)? = nil) async throws -> MyNumberCardData
static func lookupMyNumberRemainingPINAttempts(transport: any NFCTagTransport, pinType: MyNumberPINType) async throws -> Int
// Ultralight / NTAG
static func readUltralightPages(startPage: UInt8, transport: any NFCTagTransport) async throws -> Data
static func writeUltralightPage(_ page: UInt8, data: Data, memoryMap: UltralightMemoryMap? = nil, allowIrreversible: Bool = false, transport: any NFCTagTransport) async throws
static func fastReadUltralightPages(from startPage: UInt8, to endPage: UInt8, transport: any NFCTagTransport) async throws -> Data
static func compatibilityWriteUltralightPage(_ page: UInt8, data: Data, memoryMap: UltralightMemoryMap? = nil, allowIrreversible: Bool = false, transport: any NFCTagTransport) async throws
static func readNTAGSignature(transport: any NFCTagTransport) async throws -> Data
static func readNTAGCounter(counterID: UInt8 = 0x02, transport: any NFCTagTransport) async throws -> UInt32
static func authenticateUltralightPassword(password: Data, transport: any NFCTagTransport) async throws -> Data
static func readUltralightAuthenticationConfiguration(configStartPage: UInt8, transport: any NFCTagTransport) async throws -> (auth0Page: UInt8, accessBits: UInt8)
static func restoreUltralight(dump: MemoryDump, info: CardInfo, transport: any NFCTagTransport) async throws
// DESFire
static func getDESFireApplicationIDs(transport: any NFCTagTransport) async throws -> [Data]
static func selectDESFireApplication(_ aid: Data, transport: any NFCTagTransport) async throws
static func getDESFireFileIDs(transport: any NFCTagTransport) async throws -> [UInt8]
static func getDESFireFileSettings(_ fileID: UInt8, transport: any NFCTagTransport) async throws -> DESFireFileSettings
// NTAG 424 DNA (plain, unauthenticated; NDEF application selected)
static func getNTAG424FileSettings(fileID: UInt8, transport: any NFCTagTransport) async throws -> NTAG424FileSettings
static func writeNTAG424DataPlain(fileID: UInt8, offset: UInt32 = 0, data: Data, chunkSize: Int = NTAG424Commands.defaultWriteChunkSize, transport: any NFCTagTransport) async throws
// ISO 15693 (block ranges are checked against 0...255)
static func readISO15693Blocks(range: ClosedRange<Int>, transport: any NFCTagTransport) async throws -> [Data]
static func getISO15693BlockSecurityStatus(range: ClosedRange<Int>, transport: any NFCTagTransport) async throws -> [Bool]
static func configureISO15693AFI(_ afi: UInt8, permanentlyLock: Bool = false, transport: any NFCTagTransport) async throws
static func configureISO15693DSFID(_ dsfid: UInt8, permanentlyLock: Bool = false, transport: any NFCTagTransport) async throws
static func sendISO15693CustomCommand(code: Int, parameters: Data = Data(), transport: any NFCTagTransport) async throws -> Data
static func challengeISO15693(cryptoSuiteIdentifier: Int, message: Data = Data(), transport: any NFCTagTransport) async throws
static func authenticateISO15693(cryptoSuiteIdentifier: Int, message: Data, transport: any NFCTagTransport) async throws -> ISO15693SecurityResponse
static func updateISO15693Key(keyIdentifier: Int, message: Data, transport: any NFCTagTransport) async throws -> ISO15693SecurityResponse
// Type 4
static func readType4CapabilityContainer(transport: any NFCTagTransport) async throws -> Type4CC
// Export, import, diff
static func exportJSON(dump: MemoryDump, prettyPrinted: Bool = true) throws -> String
static func exportAll(dump: MemoryDump) throws -> [MemoryDump.ExportArtifact]
static func importFlipperNFC(_ text: String) throws -> MemoryDump
static func importProxmark3MFU(_ data: Data) throws -> MemoryDump
static func diffDumps(_ lhs: MemoryDump, _ rhs: MemoryDump) -> MemoryDump.DiffSummary
Deprecated names
The 1.x facade names still compile and forward to the names above: readPages, writePage, fastRead, compatibilityWrite, readSignature, readCounter, passwordAuth, readAuthConfig, getApplicationIDs, selectApplication, getFileIDs, getFileSettings, iso15693CustomCommand, iso15693Challenge, iso15693Authenticate, iso15693KeyUpdate and readType4CC. Also deprecated: the NSRange overloads of readISO15693Blocks and getISO15693BlockSecurityStatus, the lock: overloads of configureISO15693AFI / configureISO15693DSFID, and the info: overloads of readMyNumberCard and lookupMyNumberRemainingPINAttempts, whose info argument was never used. The typealiases ActiveAuthStatus, PassiveAuthStatus and ActiveAuthPublicKey name ActiveAuthenticationStatus, PassiveAuthenticationStatus and ActiveAuthenticationPublicKey. See Migrating to 2.0 for the full table.
Sessions and cancellation
scan() returns a live NFCSessionManager; keep it alive while using the transport and call invalidate() or invalidate(errorMessage:) when done. If refinement fails, the session is closed before the error is thrown. scanAndDump, readPassport and readMyNumberCard manage their own session and invalidate it before returning. The transport: overloads of readPassport and readMyNumberCard neither start nor end a session.
pollingTargets limits which tag technologies are polled, for example [.iso14443]. Each session-owning call accepts a manager:, so you can pass your own NFCSessionManager or a subclass of it; scan returns that instance. See Subclassing for the overridable hooks.
Only one scan can be pending per NFCSessionManager; a concurrent scan throws NFCError.scanInProgress. Cancelling the calling task while waiting for or connecting to a tag invalidates the session and throws CancellationError. Ending a pending scan with invalidate() throws NFCError.sessionInvalidatedByCaller. When the user dismisses the NFC sheet, failures are reported as NFCError.userCanceled and no error message is shown on the sheet. All three report error.isNFCCancellation == true. NFCSessionManager.wasCanceledByUser covers a failure that raced the user's cancel. See Errors.
Passport reads
readPassport reads EF.CardAccess, tries PACE when it is advertised, and falls back to BAC. It follows the Chip Access Procedure of ICAO 9303-11 §4.2: PACE runs before the eMRTD application is selected, and the application is then selected under Secure Messaging; with BAC the application is selected in plain first. EF.COM and EF.SOD are read first. A data group is selected when either lists it. Only the EF.SOD hash list can rule a data group out; if dataGroups contains .com but not .sod and EF.COM omits a requested group, EF.SOD is also read as the index. DG1 is read next, then the other groups, DG15 and Active Authentication. EAC-protected DG3 and DG4 are read last. Files are capped at 10 MB (DG2-DG7) or 64 KB (everything else). The call throws when nothing could be read, or when DG1 was requested but not read. Otherwise it returns a partial model, and dataGroupReadStatus records read, notPresent, unavailable, failed or notRead per group. onProgress receives localized progress messages that are also shown on the NFC sheet.
Pass DER-encoded CSCA certificates as trustAnchorsDER. Without them, Passive Authentication reports .fallback at best. See Passport for the status meanings. To build a model from stored data-group bytes without NFC, use PassportModel(rawDataGroups:cardAccessRaw:performPassiveAuthentication:trustAnchorsDER:dataGroupReadStatus:); see Models.
Logging
NFCLogConfiguration.logger = MyLogger() // any NFCLogger
NFCLogConfiguration.logPayloads = false // default
By default, frames are logged as header, lengths and status word only. With logPayloads = true, payload bytes, UIDs and block contents are included too. Even then, PIN, password and authentication exchanges (VERIFY, CHANGE REFERENCE DATA, GET CHALLENGE, the AUTHENTICATE commands, MIFARE PWD_AUTH and PACK, Ultralight C and DESFire authentication) are logged as <redacted n bytes>, and Secure Messaging APDUs log only header and status word.
Transit
readTransitBalance supports Japan IC, Rakuten Edy, WAON, nanaco, Octopus, Singapore CEPAS, Korea KS X 6924, and China T-Union. Pass the original card information and transport from NFCSessionManager.scan; do not call refineCardInfo or use CoreExtendedNFC.scan, which refines automatically. FeliCa cards are routed by system code; on the common area (FE00) the Edy, WAON and nanaco readers are tried in turn, and only .tagNotSupported (the purse's balance service is missing) moves on. ISO 7816 cards are probed for CEPAS, then KS X 6924 and T-Union. CEPAS communication, malformed-response, and history errors are propagated rather than treated as a different card family. CEPAS is skipped when SELECT 4000 is rejected with a status word or GET PURSE answers 6A81 / 6A82 / 6D00 / 6E00; any other CEPAS error, including .tagNotSupported for an unsupported purse format, is propagated. For KS X 6924 and T-Union, only NFCError.applicationNotFound(aid:) or .tagNotSupported moves on to the next reader. When no reader recognizes the card, NFCError.tagNotSupported is thrown.
The CEPAS reader provides read-only purse and recent-log access. One traditional EZ-Link card has been read successfully on iPhone Air using the CEPAS reader directly; the integrated automatic scan, offline operation, comparison with a ticket-machine reference, and other card variants remain unverified on physical cards. SimplyGo history is unsupported. See the test guide.
Transit transaction migration
TransitTransaction.amount and balanceAfter change from Int to Int?. This is a source compatibility change: callers that read these properties must unwrap them. Existing initializer calls with integer values still work. A nil amount means the record has no recognized monetary amount; a nil balance means the source does not expose a post-transaction balance. Neither should be displayed or calculated as zero. Raw CEPAS records and their type codes remain available for inspection.
if let amount = transaction.amount {
// Format in the card currency's stored unit.
showAmount(amount)
}
if let balanceAfter = transaction.balanceAfter {
showBalance(balanceAfter)
}
Existing JSON records containing numeric amounts and balances remain decodable. Encoding a nil value omits that key; JSON consumers must accept missing amount and balanceAfter keys. Numeric zero remains a known zero value.