Skip to content

Repository files navigation

NfcReaderKMP

Kotlin Compose Multiplatform License Maven Central Binary Compatibility

A powerful, easy-to-use Kotlin Multiplatform (KMP) library for reading NFC tags on Android and iOS using Compose Multiplatform.

View Full API Documentation


🚧 Work in Progress & Roadmap

This library is under active development. While we are approaching a stable v1.0.0, the core engine is already functional and powering NFC interactions on both Android and iOS.

✅ What's Ready

  • Unified KMP API: A single NfcReadManagerState that handles platform complexities under the hood.
  • Android Implementation: Robust NfcAdapter integration with a customizable ModalBottomSheet and Lottie support (via Compottie).
  • iOS Implementation: Seamless CoreNFC integration utilizing the native system scanning dialog.
  • Advanced Tag Parsing: Standards-compliant NDEF message and record parsing (Text, URI, vCard, Wi-Fi, MIME, External, Smart Poster).
  • Polished Sample App: A comprehensive :composeApp demonstrating real-world use cases:
    • Smart URIs: Automatic detection and launching of web links.
    • vCard Support: Parsing contact information for easy saving.
    • Wi-Fi Config: Extracting SSID and credentials from tags.
    • Multi-Record Support: Handles tags containing multiple NDEF records.
  • Unit Tested: Comprehensive test suite for all NDEF payload parsers.
  • Dokka Documentation: Fully documented API available here.
  • CI/CD: Automated unit testing, binary compatibility validation, and publishing infrastructure.

🛠️ What's Next

  • Official v1.0.0 release to Maven Central.
  • Instrumentation tests for Android hardware integration.

Note: We are currently at version 0.0.1. The API is stabilizing but may still undergo minor changes before the first official release. Star the repo to stay updated!


Features

  • Unified API: A single, clean API to handle NFC scanning on both platforms.
  • Compose Native: Lifecycle-aware state management that fits perfectly into your Compose UI.
  • Fully Customizable:
    • Android: Custom Bottom Sheet with support for Lottie animations (via Compottie).
    • iOS: Seamless integration with the native system NFC scanning dialog.
  • Flexible Configuration: Control timeouts, dismissal behaviors, and UI strings with a type-safe DSL.
  • Detailed Tag Info: Extract Serial Numbers, NDEF payloads, and supported technology lists.

Installation

Add the dependency to your commonMain source set in build.gradle.kts:

sourceSets {
    commonMain.dependencies {
        implementation("com.devtamuno.kmp:nfcreader:<version>")
        implementation("org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:<lifecycle-version>")
    }
}

Platform Setup

Android

  1. Add NFC permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />

iOS

  1. Add NFCReaderUsageDescription to your Info.plist.
  2. Enable the Near Field Communication Tag Reading capability in your Xcode project.
  3. Add NDEF support to the com.apple.developer.nfc.readersession.formats entitlement.

Usage

1. Create a stateful NFC route

Keep NFC manager creation and lifecycle-aware state collection at the route level. Remember the configuration so it is not recreated during recomposition:

@Composable
fun NfcReaderRoute() {
    val config = remember {
        NfcConfig(
            titleMessage = "Ready to Scan",
            subtitleMessage = "Hold your tag near the device.",
            buttonText = "Cancel",
            android = NfcConfig.AndroidOptions(
                nfcReadTimeout = 30.seconds,
                shouldDismissBottomSheetOnBackPress = true,
            ),
        )
    }

    val nfcManager = rememberNfcReadManagerState(config)
    val result by nfcManager.nfcReadResult.collectAsStateWithLifecycle()

    NfcReaderScreen(
        result = result,
        onStartScanning = nfcManager::startScanning,
    )
}

This example uses collectAsStateWithLifecycle from lifecycle-runtime-compose. You can also pass nfcScanningAnimationSlot to rememberNfcReadManagerState to replace the default Android scanning animation.

2. Render a stateless screen

Pass the result and user actions into a stateless composable. This keeps previews and UI tests independent from NFC hardware:

@Composable
fun NfcReaderScreen(
    result: NfcReadResult,
    onStartScanning: () -> Unit,
) {
    Column {
        Button(
            onClick = onStartScanning,
            enabled = result != NfcReadResult.Scanning,
        ) {
            Text("Start scanning")
        }

        when (result) {
            is NfcReadResult.Success -> {
                Text("Tag ID: ${result.data.serialNumber}")
                Text("NDEF records: ${result.data.parsedPayloads.size}")
            }
            is NfcReadResult.Error -> {
                Text("Error: ${result.message}", color = Color.Red)
                // Use result.error for typed recovery logic when needed.
            }
            NfcReadResult.Initial -> Text("Ready to scan")
            NfcReadResult.Scanning -> Text("Scanning for an NFC tag…")
            NfcReadResult.OperationCancelled -> Text("Scanning cancelled")
        }
    }
}

See the composeApp sample for complete tag metadata, structured payload cards, URI handling, custom payload fallbacks, and masked Wi-Fi credentials.


Configuration Options (NfcConfig)

NfcConfig groups configuration values by platform to make it clear where they are used, while still allowing them to be configured from common code using nested AndroidOptions and IosOptions.

Core Properties

These properties are root-level and apply to both platforms (where supported).

Property Type Default
titleMessage String Required
subtitleMessage String Required
buttonText String Required
nfcUnsupportedMessage String "NFC is not supported on this device"
nfcDisabledMessage String "NFC is disabled on this device"
nfcReadErrorMessage String "Unable to read the NFC tag"
ndefParser NdefParser NdefParser.Default
android AndroidOptions AndroidOptions()
ios IosOptions IosOptions()

Android-Specific (AndroidOptions)

Accessed via config.android.

Property Type Default Validation
nfcReadTimeout Duration 60.seconds Min 5s
nfcScanTimeoutMessage String "NFC scan timed out" Non-blank
sheetGesturesEnabled Boolean true -
shouldDismissBottomSheetOnBackPress Boolean false -
shouldDismissBottomSheetOnClickOutside Boolean false -

iOS-Specific (IosOptions)

Accessed via config.ios.

Property Type Default Validation
nfcSuccessMessage String "Tag scanned successfully" Non-blank

Note: All string properties (titleMessage, subtitleMessage, buttonText, etc.) are validated to be non-blank. On iOS, only subtitleMessage and ios.nfcSuccessMessage are used by the native system dialog. titleMessage and buttonText are required for the common UI but are ignored by CoreNFC.


Data Models

NfcTagData

  • serialNumber: The tag's unique ID as a hex-encoded string.
  • type: The tag type as an NfcTagType enum.
  • payload: A combined string representation of all payloads (fallback).
  • techList: A list of hardware technologies detected (e.g., "Mifare Classic").
  • parsedPayloads: A List<ParsedNfcPayload> containing structured data (Text, URI, Wifi, etc.).

NfcTagType

Value Description
NDEF NFC Data Exchange Format tag
NON_NDEF Tag that does not contain NDEF data
MIFARE MIFARE-based tag (Classic, Ultralight, DESFire)
ISO15693 ISO 15693 vicinity tag
ISO7816 ISO 7816-4 based smart card or tag
FELICA Sony FeliCa tag (transit/payments)

NfcReadResult

State Description
Initial No scan has been initiated yet
Scanning Actively scanning for a tag
Success(data) Tag was read successfully
Error(message, error) Scanning failed with a configured message and typed NfcError
OperationCancelled Scanning was cancelled by the user or system

Demo

Android Implementation iOS Implementation

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A Kotlin Multiplatform (KMP) library for reading NFC tags on Android and iOS using Compose Multiplatform.

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages