Swift OpenAPI Generator makes the HTTP contract part of the build
The stable generator turns an OpenAPI document into typed client calls and server requirements while keeping networking libraries replaceable.
An HTTP interface can drift even when both sides compile. A client can send a query parameter under the wrong name, or a server can change a response body without updating its documentation. The compiler has no reason to complain when each program contains its own independently maintained account of the protocol. Swift OpenAPI Generator addresses that duplication by deriving the Swift interface from the document that describes the HTTP service.
The January 31 announcement of version 1.0 makes this an established package offering for clients and servers. Its most useful design decision is the separation between generated operation types and the networking implementation. Adopting the contract does not require adopting the same HTTP library on every platform. The generated code participates in the build, and the application supplies the transport.
I find Swift's preference for explicit types persuasive here. HTTP boundaries contain plenty of information that otherwise survives only in strings and documentation. Giving that information a representation in the language makes mistakes easier to identify before a request leaves the process.
The document determines the operation
The original introduction in June 2023 describes an API-first workflow. OpenAPI expresses the HTTP method, path, parameters and responses in a machine-readable document. Schema definitions describe the bodies. A generator can then translate that description into the code that prepares requests or receives them.
This distinction matters because generating a model from a JSON example captures only part of an operation. A model does not establish where a value belongs in the request. A query parameter and a JSON property may contain the same string while requiring different HTTP encoding. OpenAPI records that placement alongside the schema. The generated operation can therefore represent more of the agreement between the caller and the service.
The stable announcement uses a greeting endpoint with an optional name parameter. Its server promises a successful JSON response containing a required message. Input optionality and the requirement on the output are separate decisions in the document. Generated Swift types carry those decisions into the handler and client interfaces.
That still leaves the accuracy of the contract with its authors. A generated client follows the supplied description. It cannot establish that an independently deployed service follows the same description. The practical improvement is narrower and useful. Application code and its serialization machinery now derive their account of the interface from one input, so changes to that input affect the next build.
Code generation belongs to the build
The generator is a Swift package plugin, with a command-line interface available in its package. The plugin creates source files in a location managed by the build system. Those files do not need to be committed. This avoids maintaining generated Swift files alongside a document that can contradict them after an incomplete regeneration step.
The initial introduction describes adding the generator package, the runtime library and a transport implementation. The target enables the plugin and depends on the runtime and transport products. An openapi.yaml file describes the service, while openapi-generator-config.yaml controls the kind of code to generate. The distinction between these files is useful. The HTTP agreement remains separate from the choice to build its client or server representation.
Build-time generation also moves work into the compilation workflow. I already find build times the least pleasant part of small Swift applications, so this is a cost to acknowledge. The benefit is that regeneration no longer depends on someone remembering a separate command before compiling a changed contract. The announcement makes no claim that this arrangement makes builds faster.
Version 1.0 supports OpenAPI 3.0 and 3.1. That expands the range of descriptions the tool can consume, but the specification version and the generator version remain different things. An application's document should declare the OpenAPI format it uses. The package dependency selects the implementation that interprets that document.
A typed call still needs a transport
The generated Client exposes methods for operations in the description. Its initializer accepts the server URL and a transport. For the greeting contract in the release announcement, an application using URLSession can call the generated method like this.
import Foundation
import OpenAPIURLSession
let client = Client(
serverURL: URL(string: "https://example.com/api")!,
transport: URLSessionTransport()
)
let response = try await client.getGreeting()
let message = try response.ok.body.json.message
This fragment assumes the greeting types have already been generated for the target. Client is generated application code, while URLSessionTransport comes from the integration package. The response access expresses an expected success status and content type. It does not collapse every possible HTTP response into a successful greeting.
The earlier introduction demonstrates switching over the response, including an undocumented status code. That remains an important conceptual distinction. Typed operations describe known outcomes, but network services can return responses outside the published contract. Application error handling still has to decide what those responses mean to its own caller.
The package split keeps this choice independent of the operation definitions. The announcement lists URLSession and AsyncHTTPClient integrations for clients. Server integrations include Vapor and Hummingbird. The runtime contains the shared definitions used by generated code and integrations, so the generator itself does not have to impose one framework's networking types throughout the application.
Server requirements expose contract changes
On the server, generation produces APIProtocol, with a requirement for each documented operation. An application supplies a conforming handler. The generated registerHandlers function connects those requirements to the chosen server transport. Parsing HTTP inputs and encoding outputs are therefore distinct from implementing the operation's business behavior.
This makes a contract change visible at the implementation boundary. Adding an operation changes what the generated protocol requires. Altering a request schema changes the types the handler receives. These are useful compiler obligations, although they do not prove the handler returns the business result the API's users expect.
Streaming adds another reason to preserve that separation. Version 1.0 supports request and response streams backed by AsyncSequence. The announcement specifically identifies event streams and large bodies that do not need complete buffering. A generated interface can therefore describe operations whose bodies arrive over time, alongside ordinary JSON values.
The supported content types also extend beyond JSON, including multipart bodies, URL-encoded forms and raw bytes. Each encoding has its own HTTP representation, while application code receives the corresponding typed values. For an engineer choosing an integration, the relevant question becomes which transport fits the application and the operations it needs. The service description can remain shared even when the client and server make different networking choices.