NFC programmeren op Android & iOS: complete handleiding voor beginners en ontwikkelaars

Leer stap-voor-stap hoe je NFC-tags leest en schrijft op Android en iOS. Eerst zonder programmeren (apps), daarna met concrete codevoorbeelden in Kotlin en Swift. Inclusief voorbeelden, best practices, troubleshooting en FAQ.

Wat is NFC en welke tags heb je nodig?

NFC (Near Field Communication) is een draadloze techniek waarmee je smartphone op enkele centimeters afstand informatie kan lezen en schrijven naar een NFC‑tag (bijv. sticker, kaart of keyfob).

Welke tags werken goed?

  • NTAG213/215/216 (NXP) — breed compatibel met Android én iOS; ondersteunen NDEF.
  • MIFARE Classic — niet compatibel met iOS; sommige Androids ondersteunen het niet. Voor maximale compatibiliteit: kies NTAG.

Capaciteit (bruikbare bytes)

  • NTAG213 ≈ 144 bytes
  • NTAG215 ≈ 504 bytes
  • NTAG216 ≈ 888 bytes (beste voor langere gegevens zoals vCards)

Benodigdheden

  • Smartphone met NFC (Android: veel modellen; iPhone: 7 of nieuwer voor lezen, iOS 13+ voor schrijven via apps).
  • NFC‑tags (bijv. stickers of kaarten).
  • Optioneel: tooling‑app (no‑code) of eigen app (met code).

NFC zonder programmeren: snel starten met apps

Wil je zonder code een tag schrijven? Gebruik een tooling‑app zoals NFC Tools (Android/iOS).

  1. Installeer de app en kies Write / Schrijven.
  2. Kies het type gegevens:
    • URL (bijv. je website of pagina met instructies)
    • Tekst (korte notitie of code)
    • vCard (contactgegevens)
    • Wi‑Fi (SSID + wachtwoord — telefoon slaat veilig op na scannen)
  3. Tik op Write en houd je telefoon boven de tag.
  4. Test: sluit de app, tik opnieuw — de actie moet direct starten.

Tip: schrijf altijd NDEF‑records; dat is de standaard die zowel Android als iOS begrijpt.

Android: NFC lezen en schrijven met code (Kotlin)

Project voorbereiden

Minimale setup (Android Studio):

  • Min SDK: 21+ is ruim voldoende.
  • Manifest‑rechten en features toevoegen.
  • Reader Mode gebruiken voor moderne, stabiele detectie.

Manifest‑rechten en features

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true"/>

(Optioneel) Intent filters

Alleen nodig als je de app wilt laten starten bij een scan (tap‑to‑launch). Voor pure in‑app scanning is Reader Mode genoeg.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="text/plain"/>
</intent-filter>
<intent-filter>
    <action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
    android:resource="@xml/nfc_tech_filter" />

Tech filter

<!-- res/xml/nfc_tech_filter.xml -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  <tech-list>
    <tech>android.nfc.tech.Ndef</tech>
    <tech>android.nfc.tech.NdefFormatable</tech>
  </tech-list>
</resources>

Lezen met Reader Mode

class MainActivity : AppCompatActivity() {

    private val nfcAdapter by lazy { NfcAdapter.getDefaultAdapter(this) }

    override fun onResume() {
        super.onResume()
        nfcAdapter?.enableReaderMode(
            this,
            { tag ->
                val ndef = Ndef.get(tag)
                val message = ndef?.cachedNdefMessage
                val text = message?.records?.joinToString("\n") { rec ->
                    when {
                        rec.tnf == NdefRecord.TNF_WELL_KNOWN &&
                        rec.type.contentEquals(NdefRecord.RTD_TEXT) -> rec.toText()
                        rec.tnf == NdefRecord.TNF_WELL_KNOWN &&
                        rec.type.contentEquals(NdefRecord.RTD_URI) -> rec.toUriString()
                        else -> "(onbekend record)"
                    }
                } ?: "Geen NDEF-bericht"
                runOnUiThread { Toast.makeText(this, text, Toast.LENGTH_LONG).show() }
            },
            NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_NFC_B or
            NfcAdapter.FLAG_READER_NFC_F or NfcAdapter.FLAG_READER_NFC_V,
            null
        )
    }

    override fun onPause() {
        super.onPause()
        nfcAdapter?.disableReaderMode(this)
    }
}

fun NdefRecord.toText(): String {
    val p = payload
    val langLen = p[0].toInt() and 0x3F
    return String(p, 1 + langLen, p.size - 1 - langLen, Charsets.UTF_8)
}

fun NdefRecord.toUriString(): String {
    return try { Uri.parse(String(payload)).toString() } catch (e: Exception) { "" }
}

Schrijven (URL‑record)

fun writeUrl(tag: Tag, url: String): Boolean {
    val uriRecord = NdefRecord.createUri(url)
    val message = NdefMessage(arrayOf(uriRecord))
    val ndef = Ndef.get(tag)
    return try {
        ndef?.connect()
        if (ndef != null && ndef.isWritable && ndef.maxSize >= message.toByteArray().size) {
            ndef.writeNdefMessage(message)
            true
        } else {
            val fmt = NdefFormatable.get(tag)
            if (fmt != null) {
                fmt.connect()
                fmt.format(message)
                true
            } else false
        }
    } catch (e: Exception) {
        false
    } finally {
        try { ndef?.close() } catch (_: Exception) {}
    }
}

Tips voor Android: niet elk toestel ondersteunt elk tagtype (MIFARE Classic kan afwijken). Test met NTAG213/215/216 en begin met een eenvoudig URL‑record.

iOS: NFC lezen en schrijven met code (Swift)

Vereisten

  • iPhone 7 of nieuwer; iOS 13+ voor NDEF schrijven.
  • Xcode project met Capabilities → Near Field Communication Tag Reading ingeschakeld.
  • NDEF‑compatibele tags (bijv. NTAG). iOS leest geen MIFARE Classic.

Lezen (NDEF)

import CoreNFC
import UIKit

class ViewController: UIViewController, NFCNDEFReaderSessionDelegate {

    var session: NFCNDEFReaderSession?

    @IBAction func startScan(_ sender: Any) {
        guard NFCNDEFReaderSession.readingAvailable else {
            showAlert("NFC niet beschikbaar op dit toestel.")
            return
        }
        session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
        session?.alertMessage = "Houd je iPhone boven de NFC-tag."
        session?.begin()
    }

    func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
        // gebruiker annuleerde of fout
    }

    func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
        let text = messages.flatMap { $0.records }.map { rec -> String in
            if let (str, _, _) = rec.wellKnownTypeTextPayload() { return "Tekst: \(str)" }
            if let url = rec.wellKnownTypeURIPayload() { return "URL: \(url.absoluteString)" }
            return "Onbekend record"
        }.joined(separator: "\n")

        DispatchQueue.main.async { [weak self] in
            self?.showAlert(text.isEmpty ? "Geen NDEF gevonden" : text)
        }
    }

    private func showAlert(_ msg: String) {
        let ac = UIAlertController(title: "NFC", message: msg, preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "OK", style: .default))
        present(ac, animated: true)
    }
}

Schrijven (URL‑record)

import CoreNFC

class WriterController: UIViewController, NFCNDEFReaderSessionDelegate {

    var session: NFCNDEFReaderSession?
    var urlToWrite: URL?

    func startWrite(urlString: String) {
        guard let url = URL(string: urlString), NFCNDEFReaderSession.readingAvailable else {
            return
        }
        urlToWrite = url
        session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false)
        session?.alertMessage = "Houd je iPhone boven de tag om \(url.absoluteString) te schrijven."
        session?.begin()
    }

    func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCTag]) {
        guard let tag = tags.first else { return }
        session.connect(to: tag) { [weak self] error in
            if let error = error { session.invalidate(errorMessage: error.localizedDescription); return }
            guard let self = self, let url = self.urlToWrite else { session.invalidate(); return }

            if case let NFCTag.miFare(miFareTag) = tag {
                miFareTag.asNFCNDEFTag { ndefTag in
                    ndefTag.queryNDEFStatus { status, capacity, err in
                        if let err = err { session.invalidate(errorMessage: err.localizedDescription); return }
                        guard status != .readOnly else { session.invalidate(errorMessage: "Tag is read‑only."); return }

                        let payload = NFCNDEFPayload.wellKnownTypeURIPayload(url: url)!
                        let message = NFCNDEFMessage(records: [payload])

                        ndefTag.writeNDEF(message) { writeErr in
                            if let writeErr = writeErr {
                                session.invalidate(errorMessage: "Schrijven mislukt: \(writeErr.localizedDescription)")
                            } else {
                                session.alertMessage = "URL geschreven! Test de tag door te tikken."
                                session.invalidate()
                            }
                        }
                    }
                }
            } else {
                session.invalidate(errorMessage: "Tagtype niet ondersteund voor NDEF.")
            }
        }
    }
}

Let op: iOS ondersteunt alleen NDEF‑compatibele tags (zoals NTAG). Voor iOS is een expliciete scan‑sessie vereist om te schrijven.

Veelgebruikte NDEF‑records (URL, vCard, Wi‑Fi, tekst)

  • URL — opent de browser; klein en universeel (Android & iOS).
  • Tekst (RTD‑TEXT) — korte instructies; payload bevat taalcode.
  • vCard / MeCard — digitale visitekaartjes. Schrijf als text/vcard of als tekstrecord.
  • Wi‑Fi — Android‑apps kunnen Wi‑Fi records schrijven/uitlezen; iOS vaak via URL/QR‑workflow.
  • App‑deeplinks — link direct naar scherm in je app (met https‑fallback).

Beveiliging, privacy en best practices

  • Korte afstand is veiliger, maar schrijf geen gevoelige data in platte tekst.
  • Gebruik altijd https en valideer tokens server‑side.
  • Maak tags read‑only als de inhoud niet mag wijzigen.
  • Dataminimalisatie: schrijf een korte ID; resolve details op je server.
  • UX: geef duidelijke instructies bij de tag (“Tik hier met je telefoon”).
  • Compatibiliteit: kies NTAG213/215/216; test op Android én iPhone.

Fouten oplossen (troubleshooting)

  • Telefoon detecteert niets: staat NFC aan? Plaats antenne (bovenkant telefoon) op de tag; verwijder dikke cases.
  • Schrijven faalt: tag te klein of read‑only; formateer of kies grotere NTAG.
  • iPhone leest geen MIFARE Classic: kies NTAG‑tags.
  • Android app triggert niet: gebruik Reader Mode en controleer permissies/filters.
  • URL opent niet op iOS: controleer https:// en dat het een NDEF‑URL record is (geen plain text).
  • Meerdere records nodig: houd totale bytes < maxSize van de tag; combineer kort en slim.

Praktische ideeën voor thuis en bedrijf

  • Digitale menukaart (URL naar menu/QR‑pagina).
  • Visitekaartje dat contact direct toevoegt.
  • Smart home: tik bij de voordeur → lichten/scene aan.
  • Productlabels: tag → specificaties, installatievideo, garantie.
  • Evenementen: stand‑acties, loting, lead capture via URL met UTM‑tags.
  • Werkvloer: machine‑instructies of onderhoudslogs via unieke tag‑ID.

FAQ: snelle antwoorden

Werkt elke NFC‑tag op iPhone en Android?

Kies NTAG213/215/216 voor beide. MIFARE Classic werkt niet op iPhone.

Kan ik een NFC‑tag beveiligen?

Je kunt tags read‑only maken. Voor echte beveiliging: schrijf een korte ID en laat de server de gevoelige data leveren na login.

Kan ik meerdere acties in één tag zetten?

Ja, via meerdere NDEF‑records (bijv. URL en tekst). Let op de geheugengrootte van de tag.

Heb ik programmeerkennis nodig?

Nee, met apps zoals NFC Tools schrijf je binnen minuten een tag. Voor geavanceerde flows (deeplinks, API’s) is een eigen app handig.

Welke iPhones kunnen schrijven?

iPhones met iOS 13+ en geschikte hardware (iPhone 7 en nieuwer) kunnen NDEF schrijven via apps.

Laatste update:

Vragen of hulp nodig met NFC‑tags of implementatie? Neem contact op met Quinzy.nl — we denken graag mee en houden het simpel en begrijpelijk.