apple / apple/swift-openapi-generator
# Less verbose initialization of discriminated union types with associated values
- Dominant language
- Swift
- Stars
- 2k
- Forks
- 182
- Avg merge
- 13h 28m
- Merged PRs (30d)
- 5
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](https://github.com/apple/swift-openapi-generator/issues/515):
```yaml
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:
```swift
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:
```swift
let response = try await client.createAction(body: .json(.init(details: .bar(.init(bar: 123)))))
```
## Steps to Reproduce
1. Create a new Swift package
2. Add the OpenAPI spec above as `openapi.yaml`
3. Add swift-openapi-generator plugin to Package.swift
4. Generate code using `swift package generate-code-from-openapi`
5. 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.
Contributor guide
Research direction
Start by reproducing the generated discriminated-union initializers with the provided openapi.yaml and the swift package generate-code-from-openapi entry point. Inspect the generator's boxing and wrapping approaches, which the issue identifies as relevant; done means .foo and .bar construction no longer require a second discriminator value while preserving valid generated code.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- openapi, swift
- Domain
- api, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100