Repository navigation
[BUG]: Swift 6 Strict Sendable conformance #2858
Description
Activity
I've submitted a fix for this issue in PR joelklabo/quicktype#1
The fix adds Swift 6 strict concurrency support:
- JSONCodingKey:
final classwithSendableconformance when--sendableis used - JSONAny:
@unchecked Sendableconformance when--sendableis used - JSONNull: uses
hash(into:)instead of deprecatedhashValuewhen--swift-5-supportis used
This should allow generated Swift code to compile cleanly under
swift-tools-version: 6.0.Tips appreciated: clawlet@lnbits.klabo.world
- JSONCodingKey:
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 markedSendable(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— noSendableconformanceclass JSONCodingKey: CodingKey— notfinal, noSendableclass JSONNull: Codable, Hashable— noSendable, and uses the deprecatedpublic var hashValueinstead ofhash(into:)
A
Sendablestruct holding non-Sendablemembers fails to compile under Swift 6 strict concurrency — exactly as reported.Root cause is in
packages/quicktype-core/src/language/Swift/SwiftRenderer.ts: thesendableoption only appends"Sendable"to the model protocol list (~line 392); the support-code emission (~lines 1168-1450, whereJSONAny/JSONCodingKey/JSONNullare written out) never checks the option.Assessment
- Severity: medium — the generated code doesn't compile, but only when both
--sendableis passed (non-default) and the project is on a Swift 6 toolchain with strict concurrency. - Difficulty: easy — localized string-emission changes: make
JSONCodingKeyfinal+Sendable, markJSONAny@unchecked Sendable, replacehashValuewithhash(into:)onJSONNull, all gated on thesendableoption. 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, Sendablealongsideclass JSONAny: Codable(no Sendable), non-finalclass JSONCodingKey: CodingKey, andclass JSONNull: Codable, Hashablewithpublic var hashValue.
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--sendableis 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.6Description
When generating Swift code with
--sendableand--struct-or-class struct, the generated model structs correctly gainSendableconformance. However, the helper classes at the bottom of the output (JSONNull,JSONCodingKey,JSONAny) are not updated for Swift 6 strict concurrency:JSONCodingKeyis a non-final class — Swift 6 errors withnon-final class 'JSONCodingKey' cannot conform to 'Sendable'JSONAnystoresAnywhich is notSendable— needs@unchecked Sendableandnonisolated(unsafe)(related: [FEATURE]: Swift - Sendable JSONAny #2598)JSONNulluses deprecatedhashValue(minor, but warning noise)This makes the output unusable with
swift-tools-version: 6.0packages out of the box.Input Data
Any JSON Schema that produces
additionalProperties/ untyped fields triggersJSONAnyusage. 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
--sendableis passed, all generated types — includingJSONCodingKey,JSONNull, andJSONAny— should compile cleanly under Swift 6 strict concurrency (swift-tools-version: 6.0).Specifically:
JSONCodingKeyshould befinal class JSONCodingKey: CodingKey, SendableJSONAnyshould be marked@unchecked Sendable(since it wrapsAny)JSONNullshould usehash(into:)instead of deprecatedhashValueCurrent Behaviour / Output
The generated struct models are correctly
Sendable, but the helper classes break the build.Steps to Reproduce
npm install -g quicktypeexample.jsonswift-tools-version: 6.0and try to buildPossible Solution
I'm currently working around this by making the following changes with
sedafter generation:final class JSONCodingKey: CodingKey, Sendableinstead ofclass JSONCodingKey: CodingKeypublic class JSONAny: Codable, @unchecked Sendablewithnonisolated(unsafe)on thevaluepropertypublic var hashValue: Intwithpublic func hash(into hasher: inout Hasher) {}onJSONNullFor point 2 this is would be safer as an enum for future extensibility