From 665189d8c831708be9c9c1b4b9c12b0730da8b39 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Mon, 3 Aug 2026 10:02:49 +0100 Subject: [PATCH 1/2] docs: document Firestore query and populateCell transforms Clarify that Firestore UI has no client-side map/filter/sort, and show query-side ordering plus limit(toLast:) for chat-style feeds. Fixes #491 --- FirebaseFirestoreUI/README.md | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/FirebaseFirestoreUI/README.md b/FirebaseFirestoreUI/README.md index b984607a284..10311ed1c56 100644 --- a/FirebaseFirestoreUI/README.md +++ b/FirebaseFirestoreUI/README.md @@ -45,6 +45,57 @@ self.dataSource = collectionView.bind(to: query) { collectionView, indexPath, sn } ``` +### Transforming and ordering data + +Firestore UI binds the **results of a Firestore query** to a table or collection +view. It does not provide client-side `map` / `filter` / `sort` transforms on +those results (Realtime Database UI's `FUISortedArray` has no Firestore +counterpart). Shape data for display in one of these places instead: + +1. **In the query** — filter, order, and limit with Firestore query APIs before + binding. Prefer this for anything that affects which documents appear or in + what order. +2. **In `populateCell`** — map fields onto cell UI when dequeuing. Prefer this + for presentation-only changes (labels, formatting, hiding empty fields). + +#### Query-side ordering and limits + +```swift +// Newest-first list +let query = Firestore.firestore() + .collection("posts") + .order(by: "createdAt", descending: true) + .limit(to: 50) + +self.dataSource = tableView.bind(to: query) { tableView, indexPath, snapshot in + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", + for: indexPath) + let data = snapshot.data() + cell.textLabel?.text = data?["title"] as? String + return cell +} +``` + +For a chat-style feed (latest N messages, oldest → newest in the list), use +`limit(toLast:)` with ascending order: + +```swift +let query = Firestore.firestore() + .collection("rooms").document(roomId).collection("messages") + .order(by: "timestamp") + .limit(toLast: 30) + +self.dataSource = tableView.bind(to: query) { tableView, indexPath, snapshot in + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", + for: indexPath) + /* populate cell */ + return cell +} +``` + +Keep queries bounded. `FUIBatchedArray` diffs the full result set on updates and +on query changes, so unbounded listeners will hurt performance. + #### FUIBatchedArray `FUIBatchedArray` powers all of the updating logic in the data source classes From b58fac77b311f23808f2f2a8b6ee6277c0f51105 Mon Sep 17 00:00:00 2001 From: Russell Wheatley Date: Mon, 3 Aug 2026 10:58:55 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- FirebaseFirestoreUI/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FirebaseFirestoreUI/README.md b/FirebaseFirestoreUI/README.md index 10311ed1c56..15c6f8f1ef1 100644 --- a/FirebaseFirestoreUI/README.md +++ b/FirebaseFirestoreUI/README.md @@ -49,7 +49,7 @@ self.dataSource = collectionView.bind(to: query) { collectionView, indexPath, sn Firestore UI binds the **results of a Firestore query** to a table or collection view. It does not provide client-side `map` / `filter` / `sort` transforms on -those results (Realtime Database UI's `FUISortedArray` has no Firestore +those results (Firebase Database UI's FUISortedArray has no Firestore counterpart). Shape data for display in one of these places instead: 1. **In the query** — filter, order, and limit with Firestore query APIs before @@ -93,8 +93,7 @@ self.dataSource = tableView.bind(to: query) { tableView, indexPath, snapshot in } ``` -Keep queries bounded. `FUIBatchedArray` diffs the full result set on updates and -on query changes, so unbounded listeners will hurt performance. +Keep queries bounded. When a query changes, FUIBatchedArray falls back to a Longest Common Subsequence (LCS) algorithm with O(N^2) complexity on the main thread to compute the diff. Unbounded queries can easily block the main thread and cause UI hangs. #### FUIBatchedArray