apple / apple/swift-openapi-generator

# Less verbose initialization of discriminated union types with associated values

Open
#748 12 comments 1 reaction 0 assignees View on GitHub
kind/feature status/triage
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.