Skip to content

ajevans99/swift-json-schema

Repository files navigation

Swift JSON Schema

CI

JSON Schema is a powerful tool for defining the structure of JSON documents. Swift JSON Schema aims to make it easier to generate JSON schema documents directly in Swift.

The OpenAI Functions Tools API is an example of a service that uses JSON schema to define the structure of API requests and responses.

Schema Generation

Swift JSON Schema enables type-safe JSON schema generation directly in Swift.

Here's a simple example of a person schema.

{
  "$id": "https://example.com/person.schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Person",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string",
      "description": "The person's first name."
    },
    "lastName": {
      "type": "string",
      "description": "The person's last name."
    },
    "age": {
      "description": "Age in years which must be equal to or greater than zero.",
      "type": "integer",
      "minimum": 0
    }
  }
}

Let's create this schema directly in Swift. RootSchema and Schema types are used to define the schema structure. The RootSchema type represents the root of the schema document, and the Schema type represents a JSON schema object.

let schema = RootSchema(
  id: "https://example.com/person.schema.json",
  schema: "https://json-schema.org/draft/2020-12/schema",
  subschema: .object(
    .annotations(title: "Person"),
    .options(
      properties: [
        "firstName": .string(
          .annotations(description: "The person's first name.")
        ),
        "lastName": .string(
          .annotations(description: "The person's last name.")
        ),
        "age": .integer(
          .annotations(description: "Age in years which must be equal to or greater than zero."),
          .options(minimum: 0)
        )
      ]
    )
  )
)

Both Schema and RootSchema conform to Codable for easy serialization

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(self)
let string = String(decoding: data, as: UTF8.self)

or deserialization

let decoder = JSONDecoder()
let data = Data(json.utf8)
let schema = try decoder.decode(Schema.self, from: data)

Result Builers

Import the JSONResultBuilders target and improve schema generation ergonomics with Swift's result builders.

@JSONSchemaBuilder var schemaRepresentation: JSONSchemaRepresentable {
  JSONObject {
    JSONProperty(key: "firstName") {
      JSONString()
        .description("The person's first name.")
    }

    JSONProperty(key: "lastName") {
      JSONString()
        .description("The person's last name.")
    }

    JSONProperty(key: "age") {
      JSONInteger()
        .description("Age in years which must be equal to or greater than zero.")
        .minimum(0)
    }
  }
  .title("Person")
}

schemaRepresentation.schema // Same `Schema` type as above for quick serialization

Installation

You can add the SwiftJSONSchema package to your project using Swift Package Manager (SPM) or Xcode.

Using Swift Package Manager (SPM)

To add SwiftJSONSchema to your project using Swift Package Manager, add the following dependency to your Package.swift file:

dependencies: [
    .package(url: "https://github.com/ajevans99/swift-json-schema", from: "1.0.0")
]

Then, include JSONSchema and/or JSONResultBuilders as a dependency for your target:

targets: [
    .target(
        name: "YourTarget",
        dependencies: [
            .product(name: "JSONSchema", package: "swift-json-schema")
        ]
    )
]

Using Xcode

  1. Open your project in Xcode.
  2. Navigate to File > Swift Packages > Add Package Dependency...
  3. Enter the repository URL: https://github.com/ajevans99/swift-json-schema
  4. Follow the prompts to add the package to your project.

Once added, you can import JSONSchema in your Swift files and start using it in your project.

Documentation

The full documentation for this library is available through the Swift Package Index.

View the documentation

Next Steps

This library is in active development. If you have any feedback or suggestions, please open an issue or pull request.

Goals for future releases include:

Detailed Macro Goal Example

Add a @Schemable attribute to a struct to generate a schema for the struct.

// Future macro-based schema generation goal example
@Schemable
struct Person: Codable {
  @SchemaOptions(description: "The person's first name.")
  let firstName: String
  
  @SchemaOptions(description: "The person's last name.")
  let lastName: String
  
  @SchemaOptions(description: "Age in years.", minimum: 0)
  let age: Int
}

The @Schemable attribute would generate a schema for the Person struct.

/// Generated property on `Person` struct by `@Schemable` attribute
@JSONSchemaBuilder static var schemaRepresentation: JSONSchemaRepresentable {
  JSONObject {
    JSONProperty(key: "firstName") {
      JSONString()
        .description("The person's first name.")
    }

    JSONProperty(key: "lastName") {
      JSONString()
        .description("The person's last name.")
    }

    JSONProperty(key: "age") {
      JSONInteger()
        .description("Age in years which must be equal to or greater than zero.")
        .minimum(0)
    }
  }
  .title("Person")
}

Validation and parsing would be handled by the library.

let person = """
{
  "firstName": "John",
  "lastName": "Doe",
  "age": 30
}
"""

let validation = try Person.validate(person)
#expect(validation == .success)
let instance = try Person.parse(person)
#expect(instance == Person(firstName: "John", lastName: "Doe", age: 30))

License

This library is released under the MIT license. See LICENSE for details.