Description
Motivation
While working with generated code for OpenAPI schemas that use discriminators (representing enums with associated values in Swift), I noticed that the current implementation requires redundant specification of the discriminator value when instantiating such objects.
Using the example from issue #515:
openapi: "3.1.0"
info:
title: Action API
version: "1.0.0"
description: API for handling actions
servers:
- url: https://api.example.com/v1
description: Production server
paths:
/actions:
post:
summary: Create a new action
operationId: createAction
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Action'
responses:
'201':
description: Action created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Action'
'400':
description: Invalid request payload
'500':
description: Internal server error
components:
schemas:
Action:
type: object
properties:
details:
$ref: '#/components/schemas/ActionDetails'
ActionCommon:
type: object
properties:
type:
type: string
required:
- type
ActionDetails:
oneOf:
- $ref: '#/components/schemas/ActionDetailsFoo'
- $ref: '#/components/schemas/ActionDetailsBar'
discriminator:
propertyName: type
mapping:
FOO: '#/components/schemas/ActionDetailsFoo'
BAR: '#/components/schemas/ActionDetailsBar'
ActionDetailsBar:
type: object
allOf:
- $ref: '#/components/schemas/ActionCommon'
- type: object
properties:
bar:
type: integer
format: int32
required:
- bar
ActionDetailsFoo:
type: object
allOf:
- $ref: '#/components/schemas/ActionCommon'
- type: object
properties:
foo:
type: string
required:
- foo
The generated initializers require specifying the discriminator value twice:
let response = try await client.createAction(body: .json(.init(details: .bar(.init(value1: .init(_type: "bar"), value2: .init(bar: 123))))))
What is even worse is that second time you are able to pass something unrelated. While it is possible to limit that at OpenAPI spec with the help of enum
and const
that will require additional work.
Expected Behavior
Since the discriminator value is already known at the call site (we're using the .foo
or .bar
static function), it would be more ergonomic if we didn't need to specify it again in the internal initialization. Something like:
let response = try await client.createAction(body: .json(.init(details: .bar(.init(bar: 123)))))
Steps to Reproduce
- Create a new Swift package
- Add the OpenAPI spec above as
openapi.yaml
- Add swift-openapi-generator plugin to Package.swift
- Generate code using
swift package generate-code-from-openapi
- Observe the generated initializers require redundant specification of the discriminator value
Swift Version
Swift version 5.9.2
Operating System
macOS 14.3
Proposed solution
It hard for me to figure if invasive changes are require to implement simplified `init() for such types, but that is how I see the solution at its best.
Alternatives considered
If for some reasons existing excessive init
is needed, the generated convenience laconic init
or init with default value for type
param is probably OK too.
Additional information
This issue is related to how the generator handles discriminated unions that represent what would naturally be Swift enums with associated values. While the current implementation works, it introduces unnecessary verbosity that could be improved.
It seems the issue is related to boxing
and wrapping
approaches that are used in the project.