GitHub - SoundBlaster/SpecificationConfig: Specification for Swift Configuration

CI Deploy DocC Swift 6.0+ Platform

A Swift Configuration wrapper powered by SpecificationCore. It builds typed config values from key-path bindings and emits diagnostics plus snapshots.

Manifest

  • Product: SpecificationConfig (Swift Package)
  • Platforms: macOS 15+, iOS 18+
  • Dependencies: swift-configuration, SpecificationCore, swift-docc-plugin

Features

  • Typed bindings from config keys to Draft via WritableKeyPath
  • Value and final specs with diagnostics and deterministic ordering
  • Provenance-aware snapshots (file/env/default/decision)
  • Decision bindings with trace metadata
  • Redaction support for secret values

Quickstart

Minimal end-to-end usage with an in-memory provider:

import Configuration
import SpecificationConfig

struct AppDraft {
    var petName: String?
}

struct AppConfig {
    let petName: String
}

enum AppConfigError: Error {
    case missingName
}

let profile = SpecProfile<AppDraft, AppConfig>(
    bindings: [
        AnyBinding(
            Binding(
                key: "pet.name",
                keyPath: \AppDraft.petName,
                decoder: ConfigReader.string
            )
        ),
    ],
    finalize: { draft in
        guard let petName = draft.petName else {
            throw AppConfigError.missingName
        }
        return AppConfig(petName: petName)
    },
    makeDraft: AppDraft.init
)

let provider = InMemoryProvider(values: [
    AbsoluteConfigKey(stringLiteral: "pet.name"): ConfigValue(stringLiteral: "Egorchi"),
])
let reader = ConfigReader(provider: provider)

let result = ConfigPipeline.build(profile: profile, reader: reader)

Configuration Sources

SpecificationConfig works with any provider from swift-configuration. The examples below show common patterns.

JSON File

JSON support is enabled by default via the JSON trait:

import Configuration
import SpecificationConfig
import SystemPackage

let provider = try await FileProvider<JSONSnapshot>(
    filePath: FilePath("/path/to/config.json")
)
let reader = ConfigReader(provider: provider)
let result = ConfigPipeline.build(profile: profile, reader: reader)

YAML File

Enable the YAML trait in your Package.swift dependency:

.package(url: "https://github.com/apple/swift-configuration", from: "1.0.0", traits: ["YAML"])

Then use YAMLSnapshot:

let provider = try await FileProvider<YAMLSnapshot>(
    filePath: FilePath("/path/to/config.yaml")
)
let reader = ConfigReader(provider: provider)
let result = ConfigPipeline.build(profile: profile, reader: reader)

Stacking Providers

Use multiple providers with precedence (first match wins):

let reader = ConfigReader(providers: [
    EnvironmentVariablesProvider(),                                    // highest priority
    try await FileProvider<JSONSnapshot>(filePath: FilePath("config.json")), // file values
    InMemoryProvider(values: ["app.timeout": 30]),                    // defaults
])

Note: TOML is not currently supported by swift-configuration.

Documentation

All details live in DocC under Sources/SpecificationConfig/Documentation.docc/.

Building and Testing

swift build -v
swift test -v
swiftformat --lint . # if installed

License

This project is licensed under the MIT License. See LICENSE for details.