Skip to content

Add VCardRecord for NFC vCard contact sharing#33

Merged
Harry-Chen merged 2 commits intonfcim:masterfrom
klaasdannen:feature/vcard-record
Mar 14, 2026
Merged

Add VCardRecord for NFC vCard contact sharing#33
Harry-Chen merged 2 commits intonfcim:masterfrom
klaasdannen:feature/vcard-record

Conversation

@klaasdannen
Copy link
Contributor

Summary

  • Adds VCardRecord extending MimeRecord for encoding/decoding vCard contacts via NFC (MIME type text/vcard)
  • Supports vCard 2.1, 3.0, and 4.0 formats with structured name, email, phone, address, organization, title, URL, and note properties
  • Handles vCard 2.1 bare type parameters (TEL;CELL: vs TEL;TYPE=CELL:), line unfolding (RFC 6350), and proper escaping of special characters in N-property components

Changes

  • lib/records/media/vcard.dart -- new VCardRecord, VCardName, VCardPhone, VCardAddress classes
  • lib/ndef.dart -- export vcard.dart
  • lib/record.dart -- register VCardRecord in defaultTypeFactory
  • test/vcard_test.dart -- 17 tests covering construction, round-trip encoding, real-world payloads, v2.1 parsing, special characters, line unfolding, and multi-record NDEF messages

Test plan

  • All 17 vcard tests pass (dart test test/vcard_test.dart)
  • dart format produces no changes
  • Existing tests unaffected

@Harry-Chen
Copy link
Contributor

@klaasdannen Thanks again! Could you rebase against the latest master branch, and put your test file into test/records/media as other files?

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class vCard (“text/vcard”) support to the NDEF library so applications can encode/decode contact cards via NFC as a dedicated VCardRecord instead of treating them as generic MimeRecords.

Changes:

  • Introduces VCardRecord plus supporting VCardName, VCardPhone, and VCardAddress models with vCard build/parse logic.
  • Registers VCardRecord in NDEFRecord.defaultTypeFactory and exports it from the public ndef.dart barrel.
  • Adds a comprehensive vcard_test.dart suite covering encoding/decoding, v2.1 type parameters, unfolding, and multi-record messages.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
lib/records/media/vcard.dart New vCard record implementation (encode/decode + helpers + models).
lib/record.dart Registers VCardRecord for media TNF type dispatch.
lib/ndef.dart Exports the new vCard record API.
test/vcard_test.dart Adds vCard-focused unit tests and round-trip coverage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +299 to +307
/// Escapes a single N-property component (`;` and `\` must be escaped).
static String _escapeComponent(String value) {
return value.replaceAll(r'\', r'\\').replaceAll(';', r'\;');
}

/// Unescapes a single N-property component.
static String _unescapeComponent(String value) {
return value.replaceAll(r'\;', ';').replaceAll(r'\\', r'\');
}
Comment on lines +401 to +423
/// Encodes the address to vCard ADR property line.
String encode() {
final value = '$poBox;$extended;$street;$city;$region;$postalCode;$country';
if (type != null) {
return 'ADR;TYPE=$type:$value';
}
return 'ADR:$value';
}

/// Decodes a vCard ADR property value string.
static VCardAddress decode(String value, String parameters) {
final parts = value.split(';');
final type = _extractTypeParam(parameters);
return VCardAddress(
type: type,
poBox: parts.isNotEmpty ? parts[0] : '',
extended: parts.length > 1 ? parts[1] : '',
street: parts.length > 2 ? parts[2] : '',
city: parts.length > 3 ? parts[3] : '',
region: parts.length > 4 ? parts[4] : '',
postalCode: parts.length > 5 ? parts[5] : '',
country: parts.length > 6 ? parts[6] : '',
);
Comment on lines +209 to +210
line = line.trim();
if (line.isEmpty) continue;
Comment on lines +17 to +21
return value
.replaceAll(r'\n', '\n')
.replaceAll(r'\,', ',')
.replaceAll(r'\;', ';')
.replaceAll(r'\\', r'\');
Comment on lines +304 to +307
/// Unescapes a single N-property component.
static String _unescapeComponent(String value) {
return value.replaceAll(r'\;', ';').replaceAll(r'\\', r'\');
}
Comment on lines +8 to +12
return value
.replaceAll(r'\', r'\\')
.replaceAll(',', r'\,')
.replaceAll(';', r'\;')
.replaceAll('\n', r'\n');
Comment on lines +18 to +22
.replaceAll(r'\n', '\n')
.replaceAll(r'\,', ',')
.replaceAll(r'\;', ';')
.replaceAll(r'\\', r'\');
}
Implements RFC 6350 vCard encoding/decoding as an NDEF media record.
Supports vCard 2.1, 3.0 and 4.0 formats with structured name, email,
phone, address, organization and other standard properties. Includes
proper escaping for N-property components and vCard 2.1 bare type
parameter parsing.
@klaasdannen klaasdannen force-pushed the feature/vcard-record branch from bd9534d to 9425947 Compare March 13, 2026 17:39
@Harry-Chen Harry-Chen merged commit 37f0538 into nfcim:master Mar 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants