Skip to content

Releases: GeoSpoof/swift-tr46

Release list

7.0.0

Choose a tag to compare

@anthonysgro anthonysgro released this 03 Nov 16:16
7.0.0
800d1ef

First release. A Swift port of tr46 7.0.0 (UTS #46, Unicode 18.0.0) and of the Punycode encoder and decoder from punycode.js 2.3.1. Results match tr46 exactly, quirks included. No dependencies.

API

  • TR46.toASCII(_:options:) -> String?: nil exactly where tr46 returns null.
  • TR46.toUnicode(_:options:) -> (domain: String, hadError: Bool).
  • TR46.Options: tr46's seven options under its names and defaults (checkHyphens, checkBidi, checkJoiners, useSTD3ASCIIRules, transitionalProcessing, verifyDNSLength, ignoreInvalidPunycode, all false), plus normalization.
  • TR46.upstreamVersion ("7.0.0") and TR46.unicodeVersion ("18.0.0").

Every type is Sendable.

Normalization modes

TR46.Normalization is a struct, so later releases can add modes; a switch over it needs default:.

  • .bundled (default): Unicode 18.0.0 NFC tables compiled into the package. Same answer on every OS. Linear time.
  • .foundation (Apple platforms only): Foundation's precomposedStringWithCanonicalMapping, using the OS's Unicode data. Its cost is quadratic in the length of a run of combining marks, so bound untrusted input in this mode.

Conformance

  • Unicode IdnaTestV2 18.0.0, read strictly: 6,394 of 6,396 lines for toUnicode, toASCII nontransitional and toASCII transitional. The 2 skipped lines hold a lone surrogate, which a Swift String cannot represent.
  • Unicode NormalizationTest 18.0.0: 20,171/20,171 lines, plus 1,094,910/1,094,910 other code points.
  • web-platform-tests at tr46's pinned commit 13748c3: toascii.json 61/61, IdnaTestV2.json 1,680/1,680.
  • Differential against tr46 7.0.0 on Node 24.12.0: 9,734 inputs under all 128 toASCII and 64 toUnicode option combinations; 1,245,952 and 622,976 results, all equal.
  • GeoSpoof fixture: 3,769/3,769 in both normalization modes.

Differences from tr46

  • A lone surrogate in toUnicode's output becomes U+FFFD, always with hadError == true.
  • Decoding has no length limit; tr46's String.fromCodePoint(...) throws above about 120,000 code points on Node 24.12.0.
  • Punycode above 1,024 code points runs in O(n log n) rather than O(n²), with the same results.

Platforms

Swift 6.0 or later, on Apple platforms and Linux. Package.swift declares no platforms:, so the deployment targets are the toolchain's minimums (with Xcode 27: macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 9.0, visionOS 1.0). CI covers Xcode 16.0, the newest Xcode on GitHub's macOS 26 image, Swift 6.0.3 and 6.4.2 on Linux, and an iOS Simulator build.

Install

dependencies: [
    .package(url: "https://github.com/GeoSpoof/swift-tr46", from: "7.0.0"),
],
targets: [
    .target(name: "MyTarget", dependencies: [.product(name: "TR46", package: "swift-tr46")]),
]

The major and minor version follow the tr46 release being ported; the patch number belongs to this package. See CHANGELOG.md.