Repository navigation
Add SwiftMeasurementCodable: stable Measurement JSON via CLDR unit identifiers - #10
Merged
Merged
Conversation
Constructing a Dimension subclass with the bare convenience initializer Unit(symbol:) fatal-errors on swift-corelibs-foundation (Foundation/Unit.swift:227 requires the designated init(symbol:converter:)). The unmapped-unit test cases now pass UnitConverterLinear(coefficient: 1); the converter is irrelevant since these units are only checked for a nil identifier. Verified green on Linux Swift 6.0 (269 tests) and macOS (269 tests).
The example claimed exact JSON bytes, but default JSONEncoder key order is not deterministic on Apple Foundation (Linux corelibs happened to match the old comment; macOS did not). Use .sortedKeys — as every test in the suite does — so the documented output is reproducible on all platforms. Corrected bytes verified against the built library.
The package-wide swift build/test already cover the new target on macOS and Linux; these add named, durable per-module checks — a Foundation-only isolation guard (the product must not depend on the SwiftMeasurement target), explicit debug+release product builds, and a filtered test run — so future edits to the shared steps can't silently drop Codable coverage.
The Foundation-allowlist grep had false positives (it blocked import Foundation with a trailing comment, import Foundation.NSString, and import Glibc) and false negatives (it missed @testable / @_implementationOnly import SwiftMeasurement). It now matches a real import of the SwiftMeasurement module directly, including attributed and scoped forms, while allowing system and Foundation-submodule imports. Also switches the standalone build from --product (which warns for an automatic library product and builds the whole package) to --target.
The README quick-start imported only SwiftMeasurementCodable but used Foundation types (Measurement, UnitMass, JSONEncoder), so it did not compile; it now imports Foundation. The protocol doc claimed symbol-based unit matching, but Foundation compares Dimension units by symbol AND converter, so a same-symbol/different-converter unit resolves to nil (verified). CodableMeasurement now documents that Equatable compares dimensionally (1 kg equals 1000 g despite different JSON) and that encode also throws for non-finite values.
Adds a test that resolves a unit through a further subclass (UnitDeepCrate: UnitCrateCount), exercising the as? Self guard that all 22 conformance files rely on; it must return nil, which it does. Previously untested because the only custom unit was final. Also factors the sortedKeys test encoder into a shared JSONEncoder.sortedKeysForTests helper, removing the duplicated inline copy from three test files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new standalone
SwiftMeasurementCodableproduct that encodes and decodes FoundationMeasurementvalues as stable, portable JSON —{"value": 21.5, "unit": "gram"}— whereunitis a CLDR 48.2 core unit identifier (the same vocabulary JavaScript'sIntl.NumberFormataccepts). Foundation's built-inMeasurementCodable shape leaks display symbols and converter internals, so it is unusable as a wire format; becauseMeasurementis alreadyCodableand cannot be re-conformed, this uses small wrapper types.The module imports only Foundation and has no dependency on the
SwiftMeasurementtarget.What's included
UnitIdentifierRepresentable— a public protocol onDimensionthat maps units to and from CLDR identifiers via two switch statements (no stored tables, no shared state). All 22 FoundationDimensiontypes conform, and consumers can conform their ownDimensionsubclasses.CodableMeasurement<UnitType>— a strictCodable/Equatable/Sendablewrapper that owns the{"value","unit"}wire shape and throwsDecodingErroron an unrecognized identifier, plus aMeasurement.codableconvenience.RawMeasurement— a lenient passthrough that never fails on an unknown identifier, with failable typed access viameasurement(as:).Testing
swift build -c releaseclean.SwiftMeasurementtarget.Notes
measurement.converted(to:)) to control the wire unit.bar) are valid CLDR but fall outside the smaller ECMA-402 subset thatIntl.NumberFormatformats.CodableMeasurement'sEquatablecompares dimensionally (likeMeasurement), so 1 kg equals 1000 g even though they encode to different JSON.