---
title: Animating UITableView with the Observe API
slug: v4-5/swift/quick-tips/animating-uitableview-with-the-observe-api
docTags: 
createdAt: 2023-10-02T17:35:37.531Z
---

The Ditto `observe` callback will fire for any changes to the underlying query. These changes can happen from your own device or a replication. There are two parameters for the callback handler, the current snapshot of documents and an *event* that gives a bit more information about how snapshots evolve after subsequent calls.

```swift
let liveQuery = myCollection.find("store_id == 'abc123'").observeLocal { [weak self] documents, event in
    print("current snapshot of documents", documents)
    print("event information", event)
}
```

The event parameter gives you information about how the live query has evolved from callback fire. It can tell you which document indices were inserted, updated, moved, or removed. The following example below shows a common pattern to parse the `event` parameter and apply `tableView.performBatchUpdates` using the indexes.

:::hint{type="info"}
The following code snippet assumes that you only have one `UITableView` section.
:::

```swift
import UIKit
import DittoSwift

class ViewController: UIViewController: {
    
    var liveQuery: DittoLiveQuery?
    @IBOutlet weak var tableView!
    var documents: [DittoDocument] = []
    
    override func viewDidLoad(){
        super.viewDidLoad();
        tableView.dataSource = self
        
        liveQuery = collection.find("store_id == 'abc123'").observeLocal { [weak self] docs, event in
            guard let `self` = self else { return }
            switch event {
            case .update(let changes):
                guard changes.insertions.count > 0 || changes.deletions.count > 0 || changes.updates.count > 0  || changes.moves.count > 0 else { return }
                DispatchQueue.main.async {
                    self.tableView.beginUpdates()
                    self.tableView.performBatchUpdates({
                        let deletionIndexPaths = changes.deletions.map { idx -> IndexPath in
                            return IndexPath(row: idx, section: 0)
                        }
                        self.tableView.deleteRows(at: deletionIndexPaths, with: .automatic)
                        let insertionIndexPaths = changes.insertions.map { idx -> IndexPath in
                            return IndexPath(row: idx, section: 0)
                        }
                        self.tableView.insertRows(at: insertionIndexPaths, with: .automatic)
                        let updateIndexPaths = changes.updates.map { idx -> IndexPath in
                            return IndexPath(row: idx, section: 0)
                        }
                        self.tableView.reloadRows(at: updateIndexPaths, with: .automatic)
                        for move in changes.moves {
                            let from = IndexPath(row: move.from, section: 0)
                            let to = IndexPath(row: move.to, section: 0)
                            self.tableView.moveRow(at: from, to: to)
                        }
                    }) { _ in }
                    // Set the tasks array backing the UITableView to the new documents
                    self.tasks = docs
                    self.tableView.endUpdates()
                }
            case .initial:
                // Set the tasks array backing the UITableView to the new documents
                self.tasks = docs
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            default: break
            }
        }
    }
}

extension ViewController: UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return documents.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let document = documents[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")
        cell.textLabel?.text = document["text"].stringValue
        return cell
    }
}
```

