PresetKit is a small Swift package for encoding and decoding portable, versioned preset documents. It provides a neutral document envelope, JSON and property list codecs, format-version validation, and canonical import error categories for host apps.
Preset files often start as ad-hoc JSON and later need format identity, version checks, metadata, size limits, and predictable import errors. Common payloads include editor layouts, render settings, automation workflows, and effect chains.
Host apps define the preset payload model, payload version policy, storage, syncing, sharing, UI, and product behavior. PresetKit only owns the reusable document and codec layer.
- Swift 6.0 or later
- No Apple-platform minimum is declared; PresetKit uses Foundation-only APIs.
Add this package to your Swift Package dependencies:
.package(url: "https://github.com/naviapps/preset-kit.git", from: "2.0.0")Then add the library product to your target:
.product(name: "PresetKit", package: "preset-kit")In the examples below, your app owns EditorLayout. PresetKit owns the document wrapper,
metadata, codecs, and import error categories.
import PresetKit
struct EditorLayout: Codable, Equatable {
var columns: Int
var showsInspector: Bool
}Define one host-owned PresetFormat for that app-owned payload:
import PresetKit
let layoutFormat = try PresetFormat(
identifier: "com.example.editor-layout"
)Use a host-owned format identifier such as a reverse-DNS string. Identifiers are dot-separated
segments, and each segment may contain ASCII letters, numbers, hyphens, or underscores.
Payloads only need Encodable for export, Decodable for import, or Codable when the
same type is used for both.
When the document format changes, set currentVersion to the version this app writes and
minimumSupportedVersion to the oldest version it can still import.
These versions describe the preset document format. Host apps decide how payload values migrate
between versions.
Create a document and export it as JSON:
import PresetKit
let layoutDocument = layoutFormat.makeDocument(
payload: EditorLayout(columns: 2, showsInspector: true),
metadata: PresetMetadata(
id: "focus",
title: "Focus",
producerVersion: "2026.1"
)
)
let jsonData = try PresetJSONCodec.encode(layoutDocument)PresetMetadata.id is an optional host-defined identifier. PresetKit stores it without assigning
sync, storage, or uniqueness semantics.
Import untrusted data with a byte limit. In these examples, importedJSONData is data read from a
user-selected file or another external source:
import PresetKit
let importedDocument = try PresetJSONCodec.decodeDocument(
EditorLayout.self,
from: importedJSONData,
matching: layoutFormat,
maximumByteCount: 1_000_000
)
let importedLayout = importedDocument.payloadPresetKit ignores unknown fields in its package-owned document envelope, format version, and
metadata so additive producers remain readable. Payload field handling remains the host payload
type's Decodable responsibility.
Use decodeDocument when the caller also needs document metadata or stored format identity:
import PresetKit
let importedDocument = try PresetJSONCodec.decodeDocument(
EditorLayout.self,
from: importedJSONData,
matching: layoutFormat,
maximumByteCount: 1_000_000
)
let importedTitle = importedDocument.metadata.title
let importedFormatVersion = importedDocument.formatVersion
let importedFormatIdentifier = importedFormatVersion.identifier
let importedVersionNumber = importedFormatVersion.versionUse decodeDocuments for document arrays and map their payload properties when only payloads
are needed. Foundation accepts JSON imports as UTF-8 or UTF-16 text.
Use PresetPropertyListCodec when a host app needs property list data instead
of JSON:
import PresetKit
let propertyListData = try PresetPropertyListCodec.encode(layoutDocument)
let propertyListDocument = try PresetPropertyListCodec.decodeDocument(
EditorLayout.self,
from: propertyListData,
matching: layoutFormat,
maximumByteCount: 1_000_000
)
let propertyListLayout = propertyListDocument.payloadJSON remains the default interchange format. Property lists are useful for Apple-platform workflows that already store or inspect plist data.
Decode methods throw PresetImportError directly, providing canonical categories for host UI and
logging:
import PresetKit
do {
let importedDocument = try PresetJSONCodec.decodeDocument(
EditorLayout.self,
from: importedJSONData,
matching: layoutFormat,
maximumByteCount: 1_000_000
)
saveImportedLayout(importedDocument.payload)
} catch {
switch error {
case .invalidDocument:
presentImportError("The selected file is not a valid preset.")
case .formatMismatch:
presentImportError("The selected file contains a different kind of preset.")
case .unsupportedFormatVersion:
presentImportError("This preset was created by an unsupported app version.")
case .inputTooLarge:
presentImportError("The selected file is too large.")
}
}PresetKit intentionally does not own:
- preset payload schemas
- payload schema evolution between host-app payload versions
- import/export UI
- persistence, syncing, or sharing flows
- validation beyond document format identity and version range
- YAML, TOML, or other formats that require external dependencies or host-specific schema choices
Those concerns should live in the host app or in a package with that direct responsibility.
Run the package check with:
make checkGitHub Actions runs the same check on pull requests and pushes to main.
See CONTRIBUTING.md. Release notes are in CHANGELOG.md.
Report vulnerabilities privately. See SECURITY.md.
PresetKit is released under the MIT License. See LICENSE.