Manage assets in local asset sources by updating metadata, removing individual assets, or deleting entire sources.
Assets in local sources can be modified or removed after they have been added. CE.SDK provides two levels of removal: individual assets within a source and entire asset sources. This guide covers how to query, update, and remove assets programmatically, and how to notify the engine when a source’s contents change. Adding assets to a source is covered in the Local Asset guide, and on iOS in the User Upload guide.
Creating a Local Asset Source#
To demonstrate editing and removal, first register a local source and populate it. Use addLocalSource(sourceID:) to create the source, then addAsset(to:asset:) for each asset.
try engine.asset.addLocalSource(sourceID: "my-uploads")
let mountainURI = baseURL.appendingPathComponent("ly.img.image/images/sample_1.jpg").absoluteStringlet mountainThumbURI = baseURL.appendingPathComponent("ly.img.image/thumbnails/sample_1.jpg").absoluteStringtry engine.asset.addAsset(to: "my-uploads", asset: AssetDefinition( id: "image-1", meta: [ "uri": mountainURI, "thumbUri": mountainThumbURI, "fillType": "//ly.img.ubq/fill/image", ], label: ["en": "Mountain Landscape"], tags: ["en": ["nature", "mountain"]],))
let oceanURI = baseURL.appendingPathComponent("ly.img.image/images/sample_2.jpg").absoluteStringlet oceanThumbURI = baseURL.appendingPathComponent("ly.img.image/thumbnails/sample_2.jpg").absoluteStringtry engine.asset.addAsset(to: "my-uploads", asset: AssetDefinition( id: "image-2", meta: [ "uri": oceanURI, "thumbUri": oceanThumbURI, "fillType": "//ly.img.ubq/fill/image", ], label: ["en": "Ocean Waves"], tags: ["en": ["nature", "water"]],))
let forestURI = baseURL.appendingPathComponent("ly.img.image/images/sample_3.jpg").absoluteStringlet forestThumbURI = baseURL.appendingPathComponent("ly.img.image/thumbnails/sample_3.jpg").absoluteStringtry engine.asset.addAsset(to: "my-uploads", asset: AssetDefinition( id: "image-3", meta: [ "uri": forestURI, "thumbUri": forestThumbURI, "fillType": "//ly.img.ubq/fill/image", ], label: ["en": "Forest Path"], tags: ["en": ["nature", "forest"]],))Each asset has a unique id, a localized label and tags for searchability, and a meta dictionary that holds the asset’s uri, thumbUri, and fillType. Point thumbUri at a small preview image so the asset library loads quickly, separate from the full-resolution uri.
Finding Assets in a Source#
Use findAssets(sourceID:query:) to query a source. AssetQueryData takes a fuzzy query string that matches labels and tags, plus page and perPage for pagination.
let result = try await engine.asset.findAssets( sourceID: "my-uploads", query: .init(query: "nature", page: 0, perPage: 100),)let assetToModify = result.assets.first { $0.id == "image-1" }print("Found \(result.total) assets; editing \(assetToModify?.label ?? "none")")The call returns an AssetQueryResult with the assets array, the total count, currentPage, and nextPage. Filter the results by id to locate the specific asset you want to edit or remove.
Updating Asset Metadata#
The Asset API has no in-place update. To change an asset’s metadata — labels, tags, or uri — remove the existing asset and add a new version that keeps the same id.
try engine.asset.removeAsset(from: "my-uploads", assetID: "image-1")try engine.asset.addAsset(to: "my-uploads", asset: AssetDefinition( id: "image-1", meta: [ "uri": mountainURI, "thumbUri": mountainThumbURI, "fillType": "//ly.img.ubq/fill/image", ], label: ["en": "Updated Mountain Photo"], tags: ["en": ["nature", "mountain", "updated"]],))Reusing the same id keeps references to the asset valid while the label, tags, and other metadata reflect the new values.
Removing an Asset from a Source#
Remove a single asset with removeAsset(from:assetID:). The asset is permanently deleted from the source, but blocks already created from it remain on the canvas — removal only affects the source’s contents.
try engine.asset.removeAsset(from: "my-uploads", assetID: "image-2")Notifying of Changes#
After modifying a source, call assetSourceContentsChanged(sourceID:). This emits an update event that subscribers — including the editor UI when present — use to refresh the assets they display.
try engine.asset.assetSourceContentsChanged(sourceID: "my-uploads")Creating a Temporary Source#
Register additional sources for temporary or session-specific assets that you can remove entirely when they are no longer needed.
let temporaryURI = baseURL.appendingPathComponent("ly.img.image/images/sample_4.jpg").absoluteStringlet temporaryThumbURI = baseURL.appendingPathComponent("ly.img.image/thumbnails/sample_4.jpg").absoluteStringtry engine.asset.addLocalSource(sourceID: "temporary-uploads")try engine.asset.addAsset(to: "temporary-uploads", asset: AssetDefinition( id: "temp-1", meta: [ "uri": temporaryURI, "thumbUri": temporaryThumbURI, "fillType": "//ly.img.ubq/fill/image", ], label: ["en": "Temporary Image"],))Removing an Entire Asset Source#
Remove a complete source and all of its assets with removeSource(sourceID:). Any UI displaying the source stops showing its content.
try engine.asset.removeSource(sourceID: "temporary-uploads")Use this when cleaning up temporary sources or when a user deletes an entire category of imported media.
Listening to Asset Source Events#
Subscribe to source lifecycle events to react when sources are added, removed, or have their contents updated — useful for analytics, cleanup, or syncing with external systems. Each event is exposed as an AsyncStream<String> of source IDs.
let addedListener = Task { for await sourceID in engine.asset.onAssetSourceAdded { print("Source added: \(sourceID)") }}let removedListener = Task { for await sourceID in engine.asset.onAssetSourceRemoved { print("Source removed: \(sourceID)") }}let updatedListener = Task { for await sourceID in engine.asset.onAssetSourceUpdated { print("Source updated: \(sourceID)") }}
try engine.asset.addLocalSource(sourceID: "event-demo-source")try engine.asset.assetSourceContentsChanged(sourceID: "event-demo-source")try engine.asset.removeSource(sourceID: "event-demo-source")
addedListener.cancel()removedListener.cancel()updatedListener.cancel()Iterate each stream inside a Task. Cancelling the Task unsubscribes, the equivalent of disposing the subscription when you no longer need events.
Best Practices#
- Query before modifying — Use
findAssets(sourceID:query:)to confirm an asset exists before removing it. - Notify after changes — Call
assetSourceContentsChanged(sourceID:)so the UI reflects edits and removals. - Cancel subscriptions — Store the listening
Tasks and cancel them when you no longer need events to avoid leaks. - Reuse IDs to update — Re-add an asset with the same
idso existing references keep working after the update. - Use descriptive IDs — Choose unique, meaningful asset IDs for reliable lookups.
Troubleshooting#
| Issue | Cause | Solution |
|---|---|---|
| Asset not found | ID mismatch or already removed | Query the source with findAssets(sourceID:query:) to list available assets |
| UI shows stale assets | Missing notification | Call assetSourceContentsChanged(sourceID:) after modifying the source |
| Cannot remove asset | Source is not a local source | Only sources created with addLocalSource(sourceID:) support asset removal |
| Events never fire | Listener cancelled or started too late | Start the listening Task before triggering the operation |
API Reference#
Methods#
| Method | Description |
|---|---|
addLocalSource(sourceID:) |
Register a local asset source you can add to and remove from |
addAsset(to:asset:) |
Add an asset to a local source |
findAssets(sourceID:query:) |
Query a source’s assets with filtering and pagination |
removeAsset(from:assetID:) |
Remove a single asset from a local source |
removeSource(sourceID:) |
Remove an entire source and all of its assets |
assetSourceContentsChanged(sourceID:) |
Notify subscribers that a source’s contents changed |
onAssetSourceAdded |
AsyncStream of IDs for sources as they are added |
onAssetSourceRemoved |
AsyncStream of IDs for sources as they are removed |
onAssetSourceUpdated |
AsyncStream of IDs for sources whose contents changed |
Properties#
| Property | Type | Description |
|---|---|---|
uri |
String | URL of the asset’s full-resolution file |
thumbUri |
String | URL of the asset’s thumbnail preview |
fillType |
String | Fill applied when the asset is inserted (//ly.img.ubq/fill/image for images) |