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.
NFC zonder programmeren: snel starten met apps
Wil je zonder code een tag schrijven? Gebruik een tooling‑app zoals NFC Tools (Android/iOS).
- Installeer de app en kies Write / Schrijven.
- 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)
- Tik op Write en houd je telefoon boven de tag.
- 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/vcardof 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 <
maxSizevan 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.
