This roadmap outlines the major priorities and phased delivery plan for CodableKit. It focuses on correctness, stability, ergonomics, and extensibility while keeping macro output predictable and fast.
- Prefer compile-time guarantees and zero/low runtime overhead
- Predictable code generation; minimal surprises for users
- Opt-in features via options; maintain backwards compatibility by default
- Clear diagnostics with actionable fix-its
- Strong test coverage (snapshots + behavior) and CI signal
- 1.5.x: Phase 1 — correctness & stability (PRs #10, #11)
- 1.6.x: Phase 2 — ergonomics & resilience
- 1.7.x: Coding Transformer — functional, composable coding pipeline (MVP)
- 1.8.x: Transformer Ecosystem — official set, docs, performance
- 2.0.0: Breaking changes window (only if truly necessary)
- Status: (PR #10, PR #11)
- Optional raw-string transcode: omit key when value is nil unless
.explicitNil- Acceptance:
- Optional property with
.transcodeRawStringencodes no key whennil - With
.explicitNil, encodes null semantics appropriately - Decode path honors defaults/optionals as configured
- Optional property with
- Acceptance:
- Reuse a single
JSONEncoder/JSONDecoderper encode/decode function- Acceptance:
- Generated code creates one encoder/decoder per function
- Tests assert behavior unchanged; micro-bench shows reduced churn
- Acceptance:
- Deterministic codegen order for nested key-paths and containers
- Acceptance:
- Snapshot tests reveal stable order across runs/platforms
- Acceptance:
- Diagnostics improvements (clearer messages, targeted fix-its) — (PR #11)
- Warn when
.useDefaultOnFailureon non-optional without default - Warn when
.explicitNilon non-optional property - Suggest
.skipSuperCodingfor classes with inheritance when superclass may be non-Codable - Add fix-it for missing type annotation
- Improve multi-binding with custom key error messaging
- Enum options validation warnings
- CodableKey custom key path validation warnings
- Acceptance:
- Missing type annotation reports actionable error and fix-it
- Multi-binding with custom key reports clear error
- Enum options validation warns appropriately
- Warn when
- Tests
- Optional transcode: encode/decode (with/without
.explicitNil) - Deterministic nested ordering snapshots
- Optional transcode: encode/decode (with/without
- Lossy decoding for arrays and sets (
.lossy)- Acceptance:
LossyArray<T>semantics: invalid items dropped, valid items decoded- Default/optional behaviors still respected (including
.useDefaultOnFailure) - Works with
Set<T>(deduplication preserved) - Composes with
.transcodeRawStringand.safeTranscodeRawString(decode lossy from transcoded payload)
- Acceptance:
- Lossy decoding for dictionaries (
.lossy)- Acceptance:
- Gracefully drop invalid entries and decode valid key/value pairs
- Acceptance:
- Size-limit guard for raw-string transcode decoding
- Acceptance:
- Reasonable default limit; configurable via option or macro-level configuration in a later patch
- Exceeding limit produces decode error or default/
nilwhen.useDefaultOnFailurepresent
- Acceptance:
- Tests
- Mixed-validity collections round-trips (Array and Set)
- Combined
.lossy+.transcodeRawStringand.safeTranscodeRawString - Guard behavior, with and without defaults and
.useDefaultOnFailure
- Problem statement
- Per-field customization (e.g., dates, numbers-from-strings, lossy mapping) often requires options or manual code.
- Some needs (like per-type date strategies) push users towards hacks and can impact performance.
- Goal: Provide a first-class, composable pipeline for coding operations inspired by functional programming.
- Core concept
CodingTransformer<Input, Output>transformsResult<Input, CodingError>→Result<Output, CodingError>.- Chainable and reusable (e.g.,
.map,.flatMap,.compose(_)). - Sendable-friendly; encourages stateless transformers or shared, cached resources (e.g., formatters).
- Symmetric support: decoding transformers, encoding transformers, or bi-directional transformers when possible.
- Integration
- Property-level:
@CodableKey(transformers: [...])to apply a pipeline during decode/encode. - Type-level defaults: optional default pipeline applied across the type, overridable per-field.
- Backwards compatibility: existing options like
.transcodeRawString,.useDefaultOnFailure,.lossymap to built-in transformers. - Diagnostics: compile-time checks for transformer type compatibility and fix-its for common mistakes.
- Property-level:
- Initial official transformers (MVP)
date(.iso8601 | .secondsSince1970 | .millisecondsSince1970 | .formatted(DateFormatter))numberFromString<T: LosslessStringConvertible>()boolFromInt(0/1)andboolFromString("true"/"false")rawStringTranscode<T: Decodable &/or Encodable>()withsafeRawStringTranscodevariantdefaultOnFailure(_:)andnilOnFailurelossyArray,lossySet,lossyDictionary- Utility transforms:
trim,nonEmpty,clamp,coalesce(_:)
- API sketch
- Protocol:
public protocol CodingTransformer { associatedtype Input associatedtype Output func transform(_ input: Result<Input, CodingError>) -> Result<Output, CodingError> }
- Composition:
extension CodingTransformer { func compose<T: CodingTransformer>(_ next: T) -> some CodingTransformer where T.Input == Output { /* ... */ } }
- Usage with macro:
@Codable struct Event { @CodableKey("date", transformers: [.date(.iso8601)]) var date: Date }
- Protocol:
- Acceptance
- Pipelines apply in declared order; deterministic codegen, with snapshots.
- Encode/decode symmetry where applicable; clear diagnostics otherwise.
- Micro-benchmarks show minimal overhead compared to hand-written equivalents.
- Documentation with examples and migration notes from options to transformers.
-
JSON date decoding strategy is not configurable per type
- Swift’s
Decoderprotocol does not exposeJSONDecoder.dateDecodingStrategy. Once insideinit(from:), you cannot change the strategy. - Recommended: configure
JSONDecoderat the call site (preferred approach). - Alternatives when call site cannot be controlled:
- Pass a
DateFormatterviadecoder.userInfoand decode dates fromStringmanually in the type. - Use wrapper types (e.g.,
ISO8601Date,MillisecondsSince1970Date) that handle per-field decoding.
- Pass a
- Avoid attempting to cast
DecodertoJSONDecoder— it is not reliable and breaks abstraction.
- Swift’s
-
Performance considerations of workarounds
userInfolookups are cheap, but manualString→Dateparsing adds per-field cost.- Always reuse static/shared
DateFormatter/ISO8601DateFormatter; constructing formatters per decode is expensive. - Raw-string transcoding incurs extra allocations (string→
Data→model). Keep payloads small; prefer nativeDatedecoding when possible. - The upcoming Coding Transformer pipeline (1.7.x) will provide official, reusable date transformers with shared formatters and minimal overhead.
-
Documentation and benchmarking
- Add README guidance and examples for the above patterns and trade-offs.
- Add micro-benchmarks comparing: call-site strategy vs
userInfovs wrapper types vs transformer pipelines.
- Official transformer set expansion
- Additional date/time variants (custom calendars/timezones), locale-aware number parsing.
- Key strategy transformer (e.g., snake_case) as a pipeline stage where applicable.
- Registry for user-defined transformers and sharing across modules.
- Performance and caching
- Shared/cached formatters; zero-allocation fast paths; reduce intermediary
Data/Stringchurn. - Benchmarks for transformer chains vs. macro options and manual code.
- Shared/cached formatters; zero-allocation fast paths; reduce intermediary
- Tooling & diagnostics
- Better compile-time validation for transformer chains and inverse-encode coverage.
- Lint-like mode to preview pipelines without codegen.
- Docs & adoption
- Cookbook of transformer recipes; migration guide from options to transformers.
- CI matrix expansion across Apple platforms and Swift toolchains.
- Additional key strategies (e.g., kebab-case) if requested
- Plugin points for custom transforms (user-supplied encode/decode hooks per field)
- Lint-like mode: dry-run expansion check with warnings only
- Better Xcode diagnostics surfacing with notes and fix-its
- Avoid breaking changes in 1.x; introduce new behavior as opt-in options
- If 2.0 is required, provide deprecation path and migration notes at least one minor version beforehand
- Code-gen is deterministic and minimal
- Clear, localized diagnostic messages with actionable fix-its
- Tests: snapshot + behavioral for all new features and bug fixes
- Performance: no regressions; micro-benchmarks for hot paths
- Security: avoid unbounded allocations; limits and sanity checks in transcode paths
- Each roadmap item tracked as an issue with label
roadmap - PRs should reference the roadmap item and include tests and docs updates
- Discussion for prioritization in GitHub Discussions or issues
- Design note (if needed)
- Implementation behind options / flags
- Tests (snapshot + behavioral)
- Docs (README + examples)
- Benchmarks (if performance-sensitive)
- Changelog entry
If you have feature requests or feedback, please open an issue with context and examples. This roadmap evolves with community input.