Structured Output

Force the model to return a typed Swift struct instead of free-form text. Guaranteed to decode with no parsing and no guard chains.

How It Works

Under the hood, runStructured() registers a hidden tool whose input schema is your type's jsonSchema. The model must call that tool to respond and cannot return free text. The SDK then decodes the tool's input directly into your Swift type.

Your prompt Model Hidden tool call Swift struct

@StructuredOutput Macro

Apply @StructuredOutput to any Codable struct and the compiler synthesizes the jsonSchema property from the stored properties automatically:

Recipe exampleSwift
@StructuredOutput
struct Recipe {
    let name: String
    let ingredients: [String]
    let steps: [String]
    let prepTimeMinutes: Int
    let note: String?   // optional (omitted from "required"
}

let agent = Agent(model: provider)
let recipe: Recipe = try await agent.runStructured("Give me a pasta recipe")

print(recipe.name)               // "Spaghetti Carbonara"
print(recipe.ingredients[0])     // "200g spaghetti"
print(recipe.prepTimeMinutes)    // 20
print(recipe.note ?? "")         // may be nil
ℹ️

@StructuredOutput (attribute) and StructuredOutput (protocol) coexist, following the same pattern Swift Data uses with @Model/Model. Xcode may show a warning about the name overlap; it compiles and runs correctly.

Type Mapping

The macro maps stored properties to JSON schema types:

Swift TypeJSON SchemaIn required?
String{"type":"string"}Yes
Int, Int64, …{"type":"integer"}Yes
Double, Float{"type":"number"}Yes
Bool{"type":"boolean"}Yes
[T] / Array<T>{"type":"array","items":{...T's schema...}}Yes
T? / Optional<T>Same as TNo
Other types{"type":"object"}Yes

Array types

Arrays of primitives work automatically. Each element's schema is derived recursively:

Swift
@StructuredOutput
struct Report {
    let title: String
    let tags: [String]       // → {"type":"array","items":{"type":"string"}}
    let scores: [Double]     // → {"type":"array","items":{"type":"number"}}
    let flags: [Bool]        // → {"type":"array","items":{"type":"boolean"}}
}

Optional fields

Optional properties are included in the schema but excluded from required, so the model can choose to omit them:

Swift
@StructuredOutput
struct ProductReview {
    let productName: String   // required
    let rating: Int           // required (1–5)
    let summary: String       // required
    let pros: [String]        // required
    let cons: [String]        // required
    let verdict: String?      // optional; model may skip
}

Manual Conformance

For nested types, polymorphic schemas, or any schema the macro cannot express, conform to StructuredOutput manually:

Manual jsonSchemaSwift
struct WeatherReport: StructuredOutput {
    let city: String
    let temperature: Double
    let condition: String
    let humidity: Int

    static var jsonSchema: JSONSchema {
        [
            "type": "object",
            "properties": [
                "city":        ["type": "string"],
                "temperature": ["type": "number",  "description": "Celsius"],
                "condition":   ["type": "string",  "enum": ["sunny","cloudy","rainy","snowy"]],
                "humidity":    ["type": "integer", "minimum": 0, "maximum": 100],
            ],
            "required": ["city", "temperature", "condition", "humidity"],
        ]
    }
}

let report: WeatherReport = try await agent.runStructured("What's the weather in London?")
print("\(report.city): \(report.temperature)°C, \(report.condition)")
💡

Use enum constraints in manual schemas to restrict the values the model can produce. This is especially useful for status fields, categories, and ratings.

Nested Types

The macro maps unknown types to {"type":"object"} without a nested schema. For structs with nested types, define jsonSchema manually to give the model the full schema:

Swift
// Nested type: define manually so the schema is complete
struct OrderSummary: StructuredOutput {
    struct LineItem: Codable {
        let product: String
        let quantity: Int
        let price: Double
    }
    let orderId: String
    let items: [LineItem]
    let total: Double

    static var jsonSchema: JSONSchema {
        [
            "type": "object",
            "properties": [
                "orderId": ["type": "string"],
                "items": [
                    "type": "array",
                    "items": [
                        "type": "object",
                        "properties": [
                            "product":  ["type": "string"],
                            "quantity": ["type": "integer"],
                            "price":    ["type": "number"],
                        ],
                        "required": ["product", "quantity", "price"],
                    ]
                ],
                "total": ["type": "number"],
            ],
            "required": ["orderId", "items", "total"],
        ]
    }
}