SafeDecoding

0.3.0

AltiAntonov/SafeDecoding

What's New

0.3.0

2026-04-11T12:16:09Z

SafeDecoding 0.3.0 adds structured issue reporting through SafeDecodingReport and SafeDecodingDiagnostics.capture without changing the existing wrapper behavior.

Added

  • SafeDecodingReport for structured issue inspection during a scoped decode
  • SafeDecodingDiagnostics.capture for returning a decoded value plus a report
  • reporting coverage for clean payloads, fallback-backed issues, mixed-wrapper ordering, nested coding paths, throw behavior, and handler composition

Changed

  • README now documents report capture and the 0.3.0 release path
  • changelog now describes 0.3.0 as an additive, non-breaking reporting release
  • wrapper behavior remains unchanged while recovery is now observable as structured data

What's Changed

Full Changelog: 0.2.0...0.3.0

SafeDecoding

Resilient JSON decoding for Swift Decodable models when real-world payloads are messy.

Swift version compatibility Platform compatibility MIT License Swift workflow

Features · Installation · Quick Start · When To Use · Good Fits · Weaker Fits · Runtime Semantics · Documentation · Testing

Features

  • @SafeDecodable for optional field failure isolation
  • SafeDecodingFallbackProvider for typed fallback values on required fields
  • @SafeFallbackDecodable for fallback-backed required-value decoding
  • missing safe fields decode to nil
  • broken safe fields do not fail the whole model
  • broken fallback-backed fields emit placeholder diagnostics and use the provider value
  • placeholder diagnostics for decode issues in 0.1.0 and the unreleased 0.3.0

The current public API is intentionally centered on:

  • SafeDecodable
  • SafeDecodingFallbackProvider
  • SafeFallbackDecodable
  • SafeDecodingReport
  • SafeDecodingDiagnostics
  • SafeDecodingIssue

Installation

0.3.0 is not tagged yet. If you want the report-capturing API before release, depend on the release branch:

dependencies: [
    .package(url: "https://github.com/AltiAntonov/SafeDecoding.git", branch: "release/0.3.0")
]

Once 0.3.0 is tagged, switch back to a versioned dependency:

dependencies: [
    .package(url: "https://github.com/AltiAntonov/SafeDecoding.git", from: "0.3.0")
]

Then add the product to your target:

.target(
    name: "YourApp",
    dependencies: [
        .product(name: "SafeDecoding", package: "SafeDecoding")
    ]
)

Quick Start

import SafeDecoding

enum UnknownRoleFallback: SafeDecodingFallbackProvider {
    static let fallbackValue = "unknown"
}

struct User: Decodable {
    let id: Int
    @SafeDecodable var name: String?
    @SafeFallbackDecodable<UnknownRoleFallback> var role: String
}

If name is missing or malformed, decoding still succeeds and name becomes nil. If role is present but malformed, decoding still succeeds and role becomes "unknown".

For example, this dirty payload still decodes:

{
  "id": 7,
  "name": 404,
  "role": 42
}

name falls back to nil, while role falls back to the typed provider value.

Reports

Use SafeDecodingDiagnostics.capture when you want structured issue inspection instead of relying on the default placeholder print output.

let result = try SafeDecodingDiagnostics.capture {
    try JSONDecoder().decode(User.self, from: data)
}

let user = result.value
let report = result.report

if report.hasIssues {
    for issue in report.issues {
        print(issue.fieldPath, issue.errorDescription)
    }
}

This keeps the decoded model usable while giving the caller explicit access to the recovered-field issues.

Typed Fallbacks

Use SafeDecodingFallbackProvider when the field is required by your model shape but upstream data is noisy.

import SafeDecoding

enum UnknownCountryFallback: SafeDecodingFallbackProvider {
    static let fallbackValue = "ZZ"
}

struct Shipment: Decodable {
    let id: String
    @SafeFallbackDecodable<UnknownCountryFallback> var destinationCountryCode: String
}

Dirty vendor payload:

{
  "id": "shp_4815",
  "destinationCountryCode": 404
}

Decoded result:

let shipment = try JSONDecoder().decode(Shipment.self, from: data)
shipment.destinationCountryCode // "ZZ"

The fallback provider is explicit and typed, so the call site makes the recovery behavior visible in the model declaration instead of burying it in custom decoding code.

When To Use

Use SafeDecoding when:

  • you consume third-party or drift-prone APIs
  • one broken field should not discard the whole payload
  • you want typed defaults for required fields without writing custom init(from:)
  • you want to stay in the Codable model

Good Fits

  • third-party APIs with inconsistent optional field quality
  • apps that want to preserve valid model data instead of failing the whole decode
  • codebases that prefer a small wrapper-based entry point over manual parsing
  • models that need explicit fallback values such as "unknown", "ZZ", or sentinel enums
  • teams that want placeholder diagnostics today and richer reporting later

Weaker Fits

  • strict backend contracts you fully control
  • schema validation workflows
  • rich reporting pipelines that need more than placeholder diagnostics
  • cases where silent fallback values would hide contract breakage you should fail fast on

Runtime Semantics

  • @SafeDecodable is scoped to optional-like wrapped values
  • @SafeFallbackDecodable uses the decoded value when decoding succeeds
  • if a fallback-backed field is present but malformed, a placeholder diagnostic is emitted and the provider value is used
  • the 0.3.0 reporting API is additive and non-breaking relative to 0.2.0
  • missing safe fields decode to nil
  • broken safe fields emit a placeholder diagnostic and fall back to nil
  • diagnostics are intentionally lightweight in 0.1.0 and the unreleased 0.3.0

Documentation

README.md is the primary package documentation for the unreleased 0.3.0 branch and remains accurate for 0.1.0 and 0.2.0 where features overlap.

Swift Package Index metadata is configured in .spi.yml so the package page can reflect the current target and author metadata cleanly.

Testing

0.1.0 ships with Swift Testing coverage for valid values, missing keys, broken values, and diagnostic capture. 0.2.0 extends that coverage to typed fallback-backed decoding behavior, and the unreleased 0.3.0 branch adds report capture coverage.

Description

  • Swift Tools 6.0.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Apr 19 2026 17:41:21 GMT-0900 (Hawaii-Aleutian Daylight Time)