Detect the MIME type of resources the engine can access and relocate embedded media to external URLs using engine.editor.getMIMEType(url:) and engine.editor.relocateResource(currentURL:relocatedURL:).
When a scene archive bundles its media, the embedded files are held in memory and referenced through internal buffer:// URIs rather than their original URLs. To process those files correctly — to persist them with the right extension, or to upload them to a CDN and produce a portable scene — you first need to know each resource’s format. getMIMEType(url:) detects the format of any resource the engine can reach, including buffer URIs, local files, and remote URLs.
This guide covers detecting a resource’s MIME type, embedding media as an in-memory buffer, finding transient resources, retrieving and filtering their MIME types, reading their bytes, and relocating them to external URLs.
Detecting the MIME Type#
Pass any URL the engine can access to getMIMEType(url:). It returns the standard MIME type string — image/jpeg for a JPEG, image/png for a PNG, and so on — downloading the resource first if it isn’t already cached.
let mimeType = try await engine.editor.getMIMEType(url: imageURL)print("Detected MIME type: \(mimeType)")Embedding a Resource#
To work with embedded media, write the raw bytes into an in-memory buffer with createBuffer() and setBufferData(url:offset:data:), then use the buffer as a block’s image fill. The engine now references the data through a buffer:// URI — the same kind of resource produced when a scene archive bundles its assets.
let imageBuffer = engine.editor.createBuffer()try engine.editor.setBufferData(url: imageBuffer, offset: 0, data: imageData)
let graphic = try engine.block.create(.graphic)try engine.block.setShape(graphic, shape: engine.block.createShape(.rect))let imageFill = try engine.block.createFill(.image)try engine.block.setURL(imageFill, property: "fill/image/imageFileURI", value: imageBuffer)try engine.block.setFill(graphic, fill: imageFill)try engine.block.appendChild(to: page, child: graphic)Finding Transient Resources#
Transient resources are embedded files whose data would be lost if the scene were exported on its own. findAllTransientResources() returns each resource’s url and size in bytes.
let transientResources = try engine.editor.findAllTransientResources()print("Found \(transientResources.count) transient resources")Retrieving MIME Types#
Call getMIMEType(url:) on each transient resource to categorize what the scene embeds. This is useful for reporting on a scene’s contents or deciding how to process each resource.
var resourcesByType: [String: Int] = [:]for resource in transientResources { let type = try await engine.editor.getMIMEType(url: resource.url) resourcesByType[type, default: 0] += 1}print("Resources by type: \(resourcesByType)")A scene may embed several formats at once — images alongside fonts and audio. The method returns standard MIME type strings such as image/jpeg, image/png, font/ttf, or font/woff2.
Filtering Resources by Type#
To process only one kind of resource, filter by the MIME type prefix. This separates image handling from font or audio handling.
var imageResources: [(url: URL, mimeType: String)] = []for resource in transientResources { let type = try await engine.editor.getMIMEType(url: resource.url) if type.hasPrefix("image/") { imageResources.append((url: resource.url, mimeType: type)) }}print("Found \(imageResources.count) image resources")Reading Buffer Data#
Once you know a buffer’s MIME type, read its bytes with getBufferLength(url:) and getBufferData(url:offset:length:). Use the MIME type to choose the correct file extension when persisting the bytes to disk.
let bufferMimeType = try await engine.editor.getMIMEType(url: imageBuffer)let length = try engine.editor.getBufferLength(url: imageBuffer)let data = try engine.editor.getBufferData(url: imageBuffer, offset: 0, length: UInt(truncating: length))
let fileExtension = switch bufferMimeType {case "image/png": "png"case "image/webp": "webp"default: "jpg"}
let fileURL = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString) .appendingPathExtension(fileExtension)try data.write(to: fileURL, options: .atomic)print("Saved \(length) bytes as \(fileURL.lastPathComponent)")Relocating Resources#
relocateResource(currentURL:relocatedURL:) updates every reference to a resource’s current URL so the scene points at a new URL instead. After uploading the bytes you read to your storage service, relocate each buffer:// URI to the hosted URL it returns. This produces a scene that references external assets instead of embedding their data.
for resource in transientResources { // Demo placeholder — in production, use the URL your storage service returns. let hostedURL = URL(string: "https://example.com/assets/\(UUID().uuidString)")! try engine.editor.relocateResource(currentURL: resource.url, relocatedURL: hostedURL)}Verifying Relocation#
After relocating every embedded resource to a hosted URL, findAllTransientResources() returns an empty array — the scene no longer carries data that would be lost on export.
let remaining = try engine.editor.findAllTransientResources()print("Transient resources remaining: \(remaining.count)")API Reference#
| Method | Description |
|---|---|
engine.editor.getMIMEType(url:) |
Returns the MIME type of the resource at the given URL, downloading it first if it isn’t cached. |
engine.editor.findAllTransientResources() |
Returns the url and byte size of every resource whose data would be lost on export, such as embedded buffers. |
engine.editor.getBufferLength(url:) |
Returns the byte length of a buffer as an NSNumber. |
engine.editor.getBufferData(url:offset:length:) |
Returns raw Data from a buffer, starting at offset for length bytes. |
engine.editor.relocateResource(currentURL:relocatedURL:) |
Updates every reference to currentURL in the scene to use relocatedURL instead. |
Troubleshooting#
MIME Type Cannot Be Determined#
getMIMEType(url:) throws when the URL can’t be parsed or the resource can’t be fetched — a missing file or an unreachable URL, for example. For a resource the engine reaches but can’t classify, it returns the string "unknown". Wrap the call in do/catch, and treat a thrown error or an "unknown" result as the cue to fall back to a default type.
No Transient Resources Found#
If findAllTransientResources() returns an empty array, the scene has no embedded media. Its resources may already reference external URLs, or no buffer-backed fills were added.
Resources Not Relocated#
If transient resources remain after relocateResource(currentURL:relocatedURL:), confirm you pass the exact URL returned by findAllTransientResources(). Bundle resources (bundle://) are internal and cannot be relocated.
Next Steps#
- Import Design from Archive — Load self-contained archive files that bundle a scene with all of its embedded assets.
- Export Options — Export scenes and work with the resulting resource data.