Search Docs
Loading...
Skip to content

Generate From Template

Turn templates into finished designs with the Swift Engine API. Load a template, populate its variables and image placeholders with your own data, and export the result to a PNG or PDF — the workflow behind batch processing, personalization, and automated design production.

A personalized greeting card generated from a template, with a photo and populated text.

10 mins
estimated time
GitHub

Template generation transforms a template into a finished design by populating data and exporting to an output format. Load a template with engine.scene.load(from:), replace its variables and placeholders with engine.variable.set(key:value:) and the block APIs, then export with engine.block.export(_:mimeType:options:).

This guide covers loading templates, populating variables, updating placeholder content, exporting to images and PDFs, and running batch generation. To merge a template into an existing scene while keeping your canvas size, see Apply Templates; for a deeper look at replacing content, see Replace Content.

Loading Templates#

Load a template as the active scene before you populate and export it. Pass a serialized string for template data from your storage, a URL for a remote or bundled .scene file with engine.scene.load(from: URL), or engine.scene.loadArchive(from:) for an archive that bundles its own assets.

Pass overrideEditorConfig: true to import the template’s registered variables and settings into the engine, so you can discover and populate them after loading.

The example loads a serialized template string it prepared from an inline demo scene. In your app, this string comes from your own storage or a template authored in the CE.SDK web editor.

// Load a template as the active scene. Pass `overrideEditorConfig: true` to
// import the template's registered variables (and settings) into the engine.
// Use a serialized string for stored data, a URL for a remote or bundled
// `.scene` file with `engine.scene.load(from: URL)`, or
// `engine.scene.loadArchive(from:)` for an archive that bundles its assets.
try await engine.scene.load(from: templateString, overrideEditorConfig: true)

Populating Variables#

Templates reference variables with {{variableName}} tokens in their text blocks, and register a variable for each token they define. Setting a variable replaces every matching token throughout the scene.

Discover Available Variables#

List the variables a template registers with engine.variable.findAll() so you know which data it expects, and read a variable’s current value with engine.variable.get(key:). findAll() reports registered variables — a template loaded with overrideEditorConfig: true imports the variables it defines, and setting a variable registers it. Variable names are case-sensitive.

// List the variables the template registers, and read a variable's value.
let variableNames = engine.variable.findAll()
let defaultRecipient = try engine.variable.get(key: "recipientName")
print("Template variables:", variableNames, "— recipientName default:", defaultRecipient)

Set Variable Values#

Assign a value to each variable with engine.variable.set(key:value:). The matching {{token}} in the template’s text updates immediately.

// Assign values that replace the matching {{token}} placeholders in text.
try engine.variable.set(key: "recipientName", value: "Alice")
try engine.variable.set(key: "message", value: "Wishing you a wonderful year ahead!")

Updating Placeholder Content#

Beyond text variables, templates contain placeholder blocks for images and other content. Discover them with engine.block.findAllPlaceholders(), or look one up by its name with engine.block.find(byName:).

// Discover every placeholder block, or look one up by its name.
let placeholders = engine.block.findAllPlaceholders()
print("Template placeholders:", placeholders.count)
if let namedImage = engine.block.find(byName: "Image").first {
print("Found image placeholder:", try engine.block.getName(namedImage))
}

Update Image Placeholders#

Read a placeholder’s fill with engine.block.getFill(_:), then point its fill/image/imageFileURI property at your image URL to swap the content.

// Swap an image placeholder's source by updating its fill's image URI.
if let imageBlock = engine.block.find(byName: "Image").first {
let fill = try engine.block.getFill(imageBlock)
try engine.block.setURL(
fill,
property: "fill/image/imageFileURI",
value: baseURL.appendingPathComponent("ly.img.image/images/sample_2.jpg"),
)
}

Exporting to Images#

Render the populated design to an image with engine.block.export(_:mimeType:options:). Find the page to export with engine.block.find(byType: .page).

// Export the populated page to a PNG image at a target resolution.
guard let page = try engine.block.find(byType: .page).first else { return }
let pngData = try await engine.block.export(
page,
mimeType: .png,
options: ExportOptions(targetWidth: 1920, targetHeight: 1080),
)
print("Exported PNG:", pngData.count, "bytes")

Configure the output through ExportOptions: targetWidth and targetHeight set the render resolution, pngCompressionLevel (0–9) trades speed for smaller PNG files, and jpegQuality (0–1) sets JPEG quality.

Exporting to PDF#

Export the whole scene to a PDF document with mimeType: .pdf. Exporting the scene rather than a single page includes every page in a multi-page document.

// Export the whole scene to a multi-page PDF document.
if let scene = try engine.scene.get() {
let pdfData = try await engine.block.export(scene, mimeType: .pdf)
print("Exported PDF:", pdfData.count, "bytes")
}

Batch Generation Workflows#

Drive a single template with many data records. Serialize the template once with engine.scene.saveToString(), then loop over your records — reloading the template, populating variables, and exporting for each one. Reloading with overrideEditorConfig: true restores the template’s own variables to their serialized defaults before each record, so their values don’t leak between exports. Loading merges variables rather than clearing them, so a variable you set that the template doesn’t define persists across reloads — remove it with engine.variable.remove(key:) if a later record shouldn’t inherit it.

// Personalize the same template once per record and export each result.
let records: [[String: String]] = [
["recipientName": "Alice", "message": "Wishing you a wonderful year ahead!"],
["recipientName": "Bob", "message": "Congratulations on the new home!"],
["recipientName": "Carol", "message": "Thank you for everything."],
]
for record in records {
// A reload with `overrideEditorConfig: true` re-imports the template's
// own variables, resetting them to their serialized defaults each record.
try await engine.scene.load(from: templateString, overrideEditorConfig: true)
for (key, value) in record {
try engine.variable.set(key: key, value: value)
}
guard let recordPage = try engine.block.find(byType: .page).first else { continue }
let recordData = try await engine.block.export(recordPage, mimeType: .png)
print("Exported \(record["recipientName"] ?? "record"):", recordData.count, "bytes")
}

Troubleshooting#

Template Fails to Load#

Confirm the URL is reachable and returns valid scene data, and that the scene format is compatible with your SDK version. Wrap load calls in do/catch to handle network or parsing errors.

Variables Not Updating#

Ensure the variable name passed to engine.variable.set(key:value:) exactly matches the {{token}} in the template. Names are case-sensitive. Use engine.variable.findAll() to list the registered variable names.

Export Returns Empty Output#

Confirm every referenced asset is reachable and that blocks are attached to the page hierarchy — orphaned blocks do not appear in exports.

Image Placeholder Not Found#

Verify the name passed to engine.block.find(byName:) matches the block’s name exactly. Names are case-sensitive. Use engine.block.findAllPlaceholders() to discover every placeholder in the scene.

API Reference#

Method Category Purpose
scene.load(from: String) Scene Load a template from a serialized string
scene.load(from: URL) Scene Load a template from a remote or bundled URL
scene.loadArchive(from: URL) Scene Load a template archive with embedded assets
scene.saveToString() Scene Serialize the scene for batch processing
scene.get() Scene Get the active scene block
variable.findAll() Variable List every registered variable in the scene
variable.set(key:value:) Variable Set (and register) a variable value
variable.get(key:) Variable Read a variable value
variable.remove(key:) Variable Remove a registered variable
block.find(byName:) Block Find blocks by name
block.find(byType:) Block Find blocks by type, such as .page
block.findAllPlaceholders() Block Discover all placeholder blocks
block.getFill(_:) Block Get a block’s fill
block.setURL(_:property:value:) Block Set a URL property, such as an image fill URI
block.export(_:mimeType:options:) Block Export a block to a PNG, JPEG, or PDF blob

Next Steps#