diff --git a/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts b/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts index a47b189df1..d6b2b95e11 100644 --- a/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts +++ b/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts @@ -386,8 +386,7 @@ export class SwiftRenderer extends ConvenienceRenderer { if ( this._options.sendable && - (!this._options.mutableProperties || !isClass) && - !this._options.objcSupport + (!this._options.mutableProperties || !isClass) ) { protocols.push("Sendable"); } @@ -501,7 +500,11 @@ export class SwiftRenderer extends ConvenienceRenderer { const isClass = this._options.useClasses || this.isCycleBreakerType(c); const structOrClass = isClass ? "class" : "struct"; const finalPrefix = - isClass && this._options.finalClasses ? "final " : ""; + isClass && + (this._options.finalClasses || + (this._options.sendable && !this._options.mutableProperties)) + ? "final " + : ""; if (isClass && this._options.objcSupport) { // [Michael Fey (@MrRooni), 2019-4-24] Swift 5 or greater, must come before the access declaration for the class. diff --git a/test/fixtures.ts b/test/fixtures.ts index b7ec881474..9485c15c8d 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -1647,6 +1647,10 @@ export const allFixtures: Fixture[] = [ new JSONFixture(languages.PythonLanguage), new JSONFixture(languages.ElmLanguage), new JSONFixture(languages.SwiftLanguage), + new JSONFixture( + languages.SwiftSendableObjectiveCSupportLanguage, + "swift-sendable-objective-c", + ), new JSONFixture(languages.ObjectiveCLanguage), new JSONFixture(languages.TypeScriptLanguage), new JSONFixture(languages.TypeScriptZodLanguage), diff --git a/test/fixtures/swift/verify-sendable.cjs b/test/fixtures/swift/verify-sendable.cjs new file mode 100644 index 0000000000..bd427bf97c --- /dev/null +++ b/test/fixtures/swift/verify-sendable.cjs @@ -0,0 +1,37 @@ +const fs = require("fs"); + +const source = fs.readFileSync("quicktype.swift", "utf8"); +const declarations = source + .split("\n") + .filter((line) => + /^(?:@objcMembers )?(?:final )?(?:class|struct|enum) /.test(line), + ); + +if (declarations.length === 0) { + throw new Error("No generated type declarations found"); +} + +if (!declarations.some((line) => line.startsWith("enum "))) { + throw new Error("No generated enum declaration found"); +} + +if ( + !declarations.some( + (line) => line.includes("class ") || line.startsWith("struct "), + ) +) { + throw new Error("No generated class or struct declaration found"); +} + +for (const declaration of declarations) { + if (!declaration.includes("Sendable")) { + throw new Error(`Generated type is not Sendable: ${declaration}`); + } + + if ( + declaration.includes("class ") && + !declaration.startsWith("@objcMembers final class ") + ) { + throw new Error(`Generated Sendable class is not final: ${declaration}`); + } +} diff --git a/test/languages.ts b/test/languages.ts index 7f80f5e3b1..f9b81522ea 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -945,6 +945,24 @@ export const SwiftLanguage: Language = { sourceFiles: ["src/language/Swift/index.ts"], }; +export const SwiftSendableObjectiveCSupportLanguage: Language = { + ...SwiftLanguage, + compileCommand: "node verify-sendable.cjs", + diffViaSchema: false, + includeJSON: ["pokedex.json"], + rendererOptions: { + ...SwiftLanguage.rendererOptions, + sendable: "true", + "struct-or-class": "class", + "objective-c-support": "true", + }, + quickTestRendererOptions: [ + ["pokedex.json", { "struct-or-class": "struct" }], + ], + runCommand: undefined, + skipMiscJSON: true, +}; + export const ObjectiveCLanguage: Language = { name: "objective-c", base: "test/fixtures/objective-c",