The Swift JSON Schema library provides a type-safe way to generate and validate JSON schema documents directly in Swift.
Checkout this example repo which uses swift-json-schema
to create type-safe OpenAI API function tool calls.
Use the power of Swift result builders to generate JSON schema documents.
@JSONSchemaBuilder var personSchema: some JSONSchemaComponent {
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")
}
Generated JSON 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
}
}
}
Use the @Schemable
macro from JSONSchemaBuilder
to automatically generate the result builders.
@Schemable
struct Person {
let firstName: String
let lastName: String?
@NumberOptions(minimum: 0, maximum: 120)
let age: Int
}
Expanded Macro
struct Person {
let firstName: String
let lastName: String?
let age: Int
// Auto-generated schema ↴
static var schema: some JSONSchemaComponent<Person> {
JSONSchema(Person.init) {
JSONObject {
JSONProperty(key: "firstName") {
JSONString()
}
.required()
JSONProperty(key: "lastName") {
JSONString()
}
JSONProperty(key: "age") {
JSONInteger()
.minimum(0)
.maximum(120)
}
.required()
}
}
}
}
extension Person: Schemable {}
@Schemable
can be applied to enums.
@Schemable
enum Status {
case active
case inactive
}
Expanded Macro
enum Status {
case active
case inactive
static var schema: some JSONSchemaComponent<Status> {
JSONString()
.enumValues {
"active"
"inactive"
}
.compactMap {
switch $0 {
case "active":
return Self.active
case "inactive":
return Self.inactive
default:
return nil
}
}
}
}
extension Status: Schemable {}
Enums with associated values are also supported using anyOf
schema composition. See the JSONSchemaBuilder documentation for more information.
Using the Schema
type, you can validate JSON data against a schema.
let schemaString = """
{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
}
}
}
"""
let schema1 = try Schema(instance: schemaString)
let result = try schema1.validate(instance: #"{"name": "Alice"}"#)
Alternatively, you can use the JSONSchemaBuilder
builders (or macros) to create a schema and validate instances.
let nameBuilder = JSONObject {
JSONProperty(key: "name") {
JSONString()
.minLength(1)
}
}
let schema = nameBuilder.defintion()
let instance1: JSONValue = ["name": "Alice"]
let instance2: JSONValue = ["name": ""]
let result1 = schema.validate(instance1)
dump(result1, name: "Instance 1 Validation Result")
let result2 = schema.validate(instance2)
dump(result2, name: "Instance 2 Validation Result")
Instance 1 Validation Result
▿ Instance 1 Validation Result: JSONSchema.ValidationResult
- isValid: true
▿ keywordLocation: #
- path: 0 elements
▿ instanceLocation: #
- path: 0 elements
- errors: nil
▿ annotations: Optional([JSONSchema.Annotation<JSONSchema.Keywords.Properties>(keyword: "properties", instanceLocation: #, schemaLocation: #/properties, absoluteSchemaLocation: nil, value: Set(["name"]))])
▿ some: 1 element
▿ JSONSchema.Annotation<JSONSchema.Keywords.Properties>
- keyword: "properties"
▿ instanceLocation: #
- path: 0 elements
▿ schemaLocation: #/properties
▿ path: 1 element
▿ JSONSchema.JSONPointer.Component.key
- key: "properties"
- absoluteSchemaLocation: nil
▿ value: 1 member
- "name"
Instance 2 Validation Result
▿ Instance 2 Validation Result: JSONSchema.ValidationResult
- isValid: false
▿ keywordLocation: #
- path: 0 elements
▿ instanceLocation: #
- path: 0 elements
▿ errors: Optional([JSONSchema.ValidationError(keyword: "properties", message: "Validation failed for keyword \'properties\'", keywordLocation: #/properties, instanceLocation: #, errors: Optional([JSONSchema.ValidationError(keyword: "minLength", message: "The string length is less than the specified \'minLength\'.", keywordLocation: #/properties/name/minLength, instanceLocation: #/name, errors: nil)]))])
▿ some: 1 element
▿ JSONSchema.ValidationError
- keyword: "properties"
- message: "Validation failed for keyword \'properties\'"
▿ keywordLocation: #/properties
▿ path: 1 element
▿ JSONSchema.JSONPointer.Component.key
- key: "properties"
▿ instanceLocation: #
- path: 0 elements
▿ errors: Optional([JSONSchema.ValidationError(keyword: "minLength", message: "The string length is less than the specified \'minLength\'.", keywordLocation: #/properties/name/minLength, instanceLocation: #/name, errors: nil)])
▿ some: 1 element
▿ JSONSchema.ValidationError
- keyword: "minLength"
- message: "The string length is less than the specified \'minLength\'."
▿ keywordLocation: #/properties/name/minLength
▿ path: 3 elements
▿ JSONSchema.JSONPointer.Component.key
- key: "properties"
▿ JSONSchema.JSONPointer.Component.key
- key: "name"
▿ JSONSchema.JSONPointer.Component.key
- key: "minLength"
▿ instanceLocation: #/name
▿ path: 1 element
▿ JSONSchema.JSONPointer.Component.key
- key: "name"
- errors: nil
- annotations: nil
Note: Dynamic references, vocabulary, and other advanced features are not yet supported. The library is still in development, and more features will be added in the future. See current unsupported tests from the JSON Schema Test Suite.
When using builders or macros, you can also parse JSON instances into Swift types.
@Schemable
enum TemperatureUnit {
case celsius
case fahrenheit
}
@Schemable
struct Weather {
let temperature: Double
let unit: TemperatureUnit
let conditions: String
}
let data = """
{
"temperature": 20,
"unit": "celsius",
"conditions": "Sunny"
}
"""
let weather: Parsed<Weather, ParseIssue> = Weather.schema.parse(instance: data)
Optionally, combine parsing and validation in a single step.
let weather: Weather = try Weather.schema.parseAndValidate(instance: data)
Shoutout to the swift-parsing library and the Point-Free Parsing series for the inspiration behind the parsing API and implementation.
The full documentation for this library is available through the Swift Package Index.
You can add the SwiftJSONSchema package to your project using Swift Package Manager (SPM) or Xcode.
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: "0.2.1")
]
Then, include JSONSchema
and/or JSONSchemaBuilder
as a dependency for your target:
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "JSONSchema", package: "swift-json-schema"),
.product(name: "JSONSchemaBuilder", package: "swift-json-schema"),
]
)
]
- Open your project in Xcode.
- Navigate to File > Swift Packages > Add Package Dependency...
- Enter the repository URL: https://github.com/ajevans99/swift-json-schema
- 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.
This library is released under the MIT license. See LICENSE for details.