This article provides an overview and how-to instructions for observing data changes within Ditto.
You can react to changes in your local store by setting up a store observer.
You can read more about store observers in Accessing Data.Store observers are useful when you want to monitor changes from your local Ditto store and react to them immediately. For instance, when your end user updates their profile, asynchronously display changes in realtime.
Using the registerObserver method, set up an observer within the store namespace enclosed with a query that specifies the collection to watch for changes, as well as your logic to handle the incoming changes.
To associate arguments with your query add them as a parameter.
Copy
Ask AI
let observer = ditto.store.registerObserver( query: "SELECT * FROM cars WHERE color = :color", arguments: [ "color": "blue" ]){ result in /* handle change */ };
To cancel a store observer, call cancel on the observer object.Once canceled, the store observer will stop processing in the background and will no longer call the provided callback.
There are some use cases where it may be required to calculate the difference
between previous and current query results. For example, when you want to update
a UI component or external HTTP system with the latest data, you may want to
know which items have been added, removed, or changed since the last time you
received the query result. The Ditto SDK provides a DittoDiffer class that
allows you to opt-in to diffing only when necessary, thereby avoiding
unnecessary performance and memory costs.However, diffing is computationally expensive. It requires storing the previous query
results in memory, leading to increased RAM usage, which can be particularly
problematic for applications handling large datasets or frequent updates.
Instead of computing diffs synchronously with every store observer update, it is
recommended to debounce diffing, processing changes out-of-band at a cadence
that best suits your use case.
Critical Memory Management: QueryResults and QueryResultItems should be treated like database cursors. Always extract the data you need immediately and then close/dematerialize them. Never store QueryResultItems directly between observer emissions as this will cause memory bloat and potential crashes.
A DittoDiffer is given query result items and compares them to the previous set of items it has received:
Copy
Ask AI
let differ = DittoDiffer()var previousDocumentIds: [String] = [] // Store only extracted IDslet observer = ditto.store.registerObserver( query: "SELECT * FROM cars") { queryResult in let diff = differ.diff(queryResult.items) // Extract current document IDs and dematerialize items let currentDocumentIds = queryResult.items.map { item in let id = item.value["_id"] as? String ?? "unknown" item.dematerialize() // Release memory after extracting data return id } // Handle deletions using stored IDs from previous emission for index in diff.deletions { let deletedId = previousDocumentIds[index] print("Deleted car with ID: \(deletedId)") } // Handle insertions using current IDs for index in diff.insertions { let insertedId = currentDocumentIds[index] print("Inserted car with ID: \(insertedId)") } // Handle updates using current IDs for index in diff.updates { let updatedId = currentDocumentIds[index] print("Updated car with ID: \(updatedId)") } // Store only the document IDs for next callback - no live references! previousDocumentIds = currentDocumentIds}
The diff() method returns a DittoDiff containing:
insertions: Indexes of new items in the new array
deletions: Indexes of items that were removed from the old array
updates: Indexes of items in the new array that were present in the old array and whose value has changed
moves: Pairs of indexes showing items that changed position
Keep references to arrays yourself: The differ doesn’t provide access to the old and new items themselves, so they need to be retained by the user if needed.
No comparison of document metadata: The differ performs a deep comparison of the value of each query result item but doesn’t take into account any metadata. Applying a series of changes to a document will not cause it to show up in updated unless those changes result in a different value from the initial state to the final state.
No async diffing: Computing a diff on a set of many query result items or very large query result items might block the device depending on its hardware.
If you’re using the legacy live query API that automatically provides diff information through event parameters, you can migrate to the newer store observer pattern with the DittoDiffer class. The differ provides equivalent functionality but requires manual management of previous results.For a complete migration guide, see Replacing Live Query Events with Store Observers.