Skip to content

[BUG]: Swift 6 Strict Sendable conformance #2858

Description

@kieran-osgood-shopify

Issue Type

TLDR; Quicktype is generating invalid Swift under the Swift 6 language compiler

Generated Swift helper classes (JSONCodingKey, JSONAny, JSONNull) don't compile under Swift 6 strict concurrency, even when --sendable is passed.

Context (Environment, Version, Language)

Input Format: JSON Schema (--src-lang schema)
Output Language: Swift

CLI, npm, or app.quicktype.io: CLI
Version: 23.2.6

Description

When generating Swift code with --sendable and --struct-or-class struct, the generated model structs correctly gain Sendable conformance. However, the helper classes at the bottom of the output (JSONNull, JSONCodingKey, JSONAny) are not updated for Swift 6 strict concurrency:

  1. JSONCodingKey is a non-final class — Swift 6 errors with non-final class 'JSONCodingKey' cannot conform to 'Sendable'
  2. JSONAny stores Any which is not Sendable — needs @unchecked Sendable and nonisolated(unsafe) (related: [FEATURE]: Swift - Sendable JSONAny #2598)
  3. JSONNull uses deprecated hashValue (minor, but warning noise)

This makes the output unusable with swift-tools-version: 6.0 packages out of the box.

Input Data

Any JSON Schema that produces additionalProperties / untyped fields triggers JSONAny usage. Minimal example:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Example",
  "type": "object",
  "properties": {
    "data": {
      "type": "object",
      "additionalProperties": true
    }
  }
}

Expected Behaviour / Output

When --sendable is passed, all generated types — including JSONCodingKey, JSONNull, and JSONAny — should compile cleanly under Swift 6 strict concurrency (swift-tools-version: 6.0).

Specifically:

  • JSONCodingKey should be final class JSONCodingKey: CodingKey, Sendable
  • JSONAny should be marked @unchecked Sendable (since it wraps Any)
  • JSONNull should use hash(into:) instead of deprecated hashValue

Current Behaviour / Output

error: non-final class 'JSONCodingKey' cannot conform to 'Sendable'; use '@unchecked Sendable'
warning: 'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'JSONNull' to 'Hashable' by implementing 'hash(into:)' instead

The generated struct models are correctly Sendable, but the helper classes break the build.

Steps to Reproduce

  1. npm install -g quicktype
  2. Save the minimal JSON Schema above as example.json
  3. Run:
    quicktype \
      --lang swift \
      --src-lang schema \
      --src example.json \
      --access-level public \
      --struct-or-class struct \
      --coding-keys \
      --sendable \
      -o Models.swift
  4. Create a Swift package with swift-tools-version: 6.0 and try to build
 mkdir MyPackage && cd MyPackage
 swift package init --type library --name MyPackage
 sed -i '' 's/swift-tools-version: .*/swift-tools-version: 6.0/' Package.swift
 cp ../Models.swift Sources/MyPackage/Models.swift
 swift build

Possible Solution

I'm currently working around this by making the following changes with sed after generation:

  1. Emit final class JSONCodingKey: CodingKey, Sendable instead of class JSONCodingKey: CodingKey
  2. Emit public class JSONAny: Codable, @unchecked Sendable with nonisolated(unsafe) on the value property
  3. Replace public var hashValue: Int with public func hash(into hasher: inout Hasher) {} on JSONNull

For point 2 this is would be safer as an enum for future extensibility

public enum JSONValue: Codable, Sendable {
      case null
      case bool(Bool)
      case int(Int64)
      case double(Double)
      case string(String)
      case array([JSONValue])
      case object([String: JSONValue])
  }

Activity

  1. joelklabo commented on Feb 15, 2026

    @joelklabo

    I've submitted a fix for this issue in PR joelklabo/quicktype#1

    The fix adds Swift 6 strict concurrency support:

    • JSONCodingKey: final class with Sendable conformance when --sendable is used
    • JSONAny: @unchecked Sendable conformance when --sendable is used
    • JSONNull: uses hash(into:) instead of deprecated hashValue when --swift-5-support is used

    This should allow generated Swift code to compile cleanly under swift-tools-version: 6.0.

    Tips appreciated: clawlet@lnbits.klabo.world

  2. schani commented on Jul 6, 2026

    @schani
    Member

    Triage sweep note (assisted by Claude): confirmed reproducible on current master (v23.3.0, 68ecb284).

    Root cause / findings

    With --sendable, the generated model type does get marked Sendable (e.g. public struct Models: Codable, Sendable), but it holds an [String: JSONAny]? property, and the support-code types aren't Sendable-safe:

    • class JSONAny: Codable — no Sendable conformance
    • class JSONCodingKey: CodingKey — not final, no Sendable
    • class JSONNull: Codable, Hashable — no Sendable, and uses the deprecated public var hashValue instead of hash(into:)

    A Sendable struct holding non-Sendable members fails to compile under Swift 6 strict concurrency — exactly as reported.

    Root cause is in packages/quicktype-core/src/language/Swift/SwiftRenderer.ts: the sendable option only appends "Sendable" to the model protocol list (~line 392); the support-code emission (~lines 1168-1450, where JSONAny/JSONCodingKey/JSONNull are written out) never checks the option.

    Assessment

    • Severity: medium — the generated code doesn't compile, but only when both --sendable is passed (non-default) and the project is on a Swift 6 toolchain with strict concurrency.
    • Difficulty: easy — localized string-emission changes: make JSONCodingKey final + Sendable, mark JSONAny @unchecked Sendable, replace hashValue with hash(into:) on JSONNull, all gated on the sendable option. Needs matching snapshot/fixture updates. Note there's already a candidate fix from the reporter at fix(swift): Add Swift 6 strict concurrency support for helper classes joelklabo/quicktype#1 (their fork) that looks like a reasonable starting point.

    Reproduction

    $ node dist/index.js --lang swift --src-lang schema --src example.json \
        --access-level public --struct-or-class struct --coding-keys --sendable -o Models.swift
    
    {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "title": "Example",
      "type": "object",
      "properties": {
        "data": { "type": "object", "additionalProperties": true }
      }
    }

    Output contains public struct Example: Codable, Sendable alongside class JSONAny: Codable (no Sendable), non-final class JSONCodingKey: CodingKey, and class JSONNull: Codable, Hashable with public var hashValue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions