Converting between data and Swift types

Learn about the type responsible for converting between binary data and Swift types.

Converting-between-data-and-Swift-types.md

Overview

The Converter type is a structure defined in the runtime library and is used by both the client and server generated code to perform conversions between binary data and Swift types.

Most of the functionality of Converter is implemented as helper methods in extensions:

Some helper methods can be reused between client and server code, such as headers, but most can’t. It’s important that we only generalize (move helper methods into common extensions) if the client and server variants would have been exact copies. However, if there are differences, prefer to keep them separate and optimize each variant (for client or server) separately.

The converter, it contains helper methods for all the supported combinations of an schema location, a “coding strategy” and a Swift type.

Codable and coders

The project uses multiple encoder and decoder implementations that all utilize the Codable conformance of generated and built-in types.

At the time of writing, the list of coders used is as follows.

FormatEncoderDecoderSupported in
JSONFoundation.JSONEncoderFoundation.JSONDecoderBodies, headers
URI (†)OpenAPIRuntime.URIEncoderOpenAPIRuntime.URIDecoderPath, query, headers
Plain textOpenAPIRuntime.StringEncoderOpenAPIRuntime.StringDecoderBodies

†: Configurable implementation of variable expansion from URI Template (RFC 6570), the application/x-www-form-urlencoded serialization from RFC 1866, and OpenAPI 3.0.3. For details of the supported combinations, review Supported OpenAPI features.

While the generator attempts to catch invalid inputs at generation time, there are still combinations of Codable types and locations that aren’t compatible, and will only get caught at runtime by the specific coder implementation. For example, one could ask the StringEncoder to encode an array, but the encoder will throw an error, as containers are not supported in that encoder.

Dimensions of helper methods

Below is a list of the “dimensions” across which the helper methods differ:

  • Client/server represents whether the code is needed by the client, server, or both (“common”).

  • Set/get represents whether the generated code sets or gets the value.

  • Schema location refers to one of the several places where schemas can be used in OpenAPI documents. Values:

    • request path parameters

    • request query items

    • request header fields

    • request body

    • response header fields

    • response body

  • Coding strategy represents the chosen coder to convert between the Swift type and data. Supported options:

    • JSON

      • example content type: application/json and any with the +json suffix

      • {"color": "red", "power": 24}

    • URI

      • example: query, path, header parameters

      • color=red&power=24

    • urlEncodedForm

      • example: request body with the application/x-www-form-urlencoded content type

      • greeting=Hello+world

    • multipart

      • example: request body with the multipart/form-data content type

      • part 1: {"color": "red", "power": 24}, part 2: greeting=Hello+world

    • binary

      • example: application/octet-stream

      • serves as the fallback for content types that don’t have more specific handling

      • doesn’t transform the binary data, just passes it through

  • Optional/required represents whether the method works with optional values. Values:

    • required represents a special overload only for required values

    • optional represents a special overload only for optional values

    • both represents a special overload that works for optional values without negatively impacting passed-in required values (for example, setters)

Helper method variants

Together, the dimensions are enough to deterministically decide which helper method on the converter should be used.

In the list below, each row represents one helper method.

The helper method naming convention can be described as:

method name: {set,get}{required/optional/omit if both}{location}As{strategy}
method parameters: value or type of value
Client/serverSet/getSchema locationCoding strategyOptional/requiredMethod name
commonsetheader fieldURIbothsetHeaderFieldAsURI
commonsetheader fieldJSONbothsetHeaderFieldAsJSON
commongetheader fieldURIoptionalgetOptionalHeaderFieldAsURI
commongetheader fieldURIrequiredgetRequiredHeaderFieldAsURI
commongetheader fieldJSONoptionalgetOptionalHeaderFieldAsJSON
commongetheader fieldJSONrequiredgetRequiredHeaderFieldAsJSON
clientsetrequest pathURIrequiredrenderedPath
clientsetrequest queryURIbothsetQueryItemAsURI
clientsetrequest bodyJSONoptionalsetOptionalRequestBodyAsJSON
clientsetrequest bodyJSONrequiredsetRequiredRequestBodyAsJSON
clientsetrequest bodybinaryoptionalsetOptionalRequestBodyAsBinary
clientsetrequest bodybinaryrequiredsetRequiredRequestBodyAsBinary
clientsetrequest bodyurlEncodedFormoptionalsetOptionalRequestBodyAsURLEncodedForm
clientsetrequest bodyurlEncodedFormrequiredsetRequiredRequestBodyAsURLEncodedForm
clientsetrequest bodymultipartrequiredsetRequiredRequestBodyAsMultipart
clientgetresponse bodyJSONrequiredgetResponseBodyAsJSON
clientgetresponse bodybinaryrequiredgetResponseBodyAsBinary
clientgetresponse bodymultipartrequiredgetResponseBodyAsMultipart
servergetrequest pathURIrequiredgetPathParameterAsURI
servergetrequest queryURIoptionalgetOptionalQueryItemAsURI
servergetrequest queryURIrequiredgetRequiredQueryItemAsURI
servergetrequest bodyJSONoptionalgetOptionalRequestBodyAsJSON
servergetrequest bodyJSONrequiredgetRequiredRequestBodyAsJSON
servergetrequest bodybinaryoptionalgetOptionalRequestBodyAsBinary
servergetrequest bodybinaryrequiredgetRequiredRequestBodyAsBinary
servergetrequest bodyurlEncodedFormoptionalgetOptionalRequestBodyAsURLEncodedForm
servergetrequest bodyurlEncodedFormrequiredgetRequiredRequestBodyAsURLEncodedForm
servergetrequest bodymultipartrequiredgetRequiredRequestBodyAsMultipart
serversetresponse bodyJSONrequiredsetResponseBodyAsJSON
serversetresponse bodybinaryrequiredsetResponseBodyAsBinary
serversetresponse bodymultipartrequiredsetResponseBodyAsMultipart