Вопрос или проблема
Я реализую пагинацию в своем приложении iOS на Swift с Firebase Firestore и сталкиваюсь с проблемами обратной навигации. При нажатии кнопки “предыдущая страница” отображаются более новые элементы вместо старых.
Вот моя текущая реализация:
func loadNextPage() {
guard let lastDocument = lastDocument else { return }
isLoading = true
currentPage += 1
var query = db.collection("items")
.order(by: "timestamp", descending: true)
.start(afterDocument: lastDocument)
.limit(to: itemsPerPage)
// Применить фильтры...
query.getDocuments { [weak self] snapshot, error in
// Обработать результаты...
self.items = snapshot.documents.compactMap { self.documentToItem($0) }
self.lastDocument = snapshot.documents.last
}
}
func loadPreviousPage() {
guard !isLoading else { return }
isLoading = true
currentPage -= 1
var query = db.collection("items")
.order(by: "timestamp", descending: true)
if let firstItem = items.first {
query = query.whereField("timestamp", isGreaterThan: firstItem.timestamp)
.limit(to: itemsPerPage)
}
// Применить фильтры...
query.getDocuments { [weak self] snapshot, error in
// Обработать результаты...
self.items = snapshot.documents.compactMap { self.documentToItem($0) }
self.lastDocument = snapshot.documents.last
}
}
Вот что я пробовал до сих пор:
-
Используя start(after:) с временной меткой первого элемента:
query = query.start(after: [firstItem.timestamp])
-
Используя end(before:) с временной меткой первого элемента:
query = query.end(before: [firstItem.timestamp])
-
Изменяя направление сортировки для предыдущей страницы:
query = db.collection("items") .order(by: "timestamp", descending: false)
-
Используя whereField с разными сравнениями:
query = query.whereField("timestamp", isLessThan: firstItem.timestamp)
Пагинация отлично работает при переходе вперед, но при попытке вернуться назад:
- Иногда отображает те же элементы, что и на следующей странице
- Иногда прыгает на первую страницу
- Иногда показывает более новые элементы вместо старых
Моя структура данных в Firestore:
Коллекция с документами
Каждый документ имеет поле временной метки (временная метка сервера)
Использование убывающего порядка по временной метке
Ограничение до N элементов на страницу
Как я могу реализовать правильную обратную пагинацию, которая отображает предыдущие N элементов хронологически?
Ответ или решение
Для реализации корректной пагинации в вашем приложении на Swift с использованием Firebase Firestore, необходимо рассмотреть некоторые ключевые моменты, которые могут вызвать проблемы при навигации назад. Судя по вашему описанию проблемы, вы уже предприняли несколько попыток исправить ситуацию, но результат вас не устроил. Давайте подробно разберем вашу реализацию и предоставим рекомендации по устранению проблемы.
Понимание проблемы
Проблема с навигацией назад в вашем случае связана с тем, как вы осуществляете запросы к Firestore. В частности, при загрузке предыдущей страницы вы используете фильтры, которые не совсем подходят для целей, которые вы хотите достичь. Вместо того чтобы просто возвращать предыдущие документы, текущая логика запросов может приводить к неправильному набору данных.
Текущая реализация
-
Предыдущая страница: В функции
loadPreviousPage()
, вы используете следующую логику:var query = db.collection("items") .order(by: "timestamp", descending: true) if let firstItem = items.first { query = query.whereField("timestamp", isGreaterThan: firstItem.timestamp) .limit(to: itemsPerPage) }
Используя
isGreaterThan
, вы фактически запрашиваете элементы с более поздними временными метками, что противоречит вашему намерению получить более ранние элементы.
Рекомендации по решению проблемы
-
Используйте
endBefore
: ВместоisGreaterThan
, вы можете использоватьendBefore
для вашего запроса. Это даст вам документы, которые были перед первым элементом текущей страницы. Измененный запрос будет выглядеть следующим образом:if let firstItem = items.first { query = query.end(beforeDocument: firstItem.document) // Не забывайте, что firstItem должен быть документом Firestore .limit(to: itemsPerPage) }
-
Сохранение указателя последнего документа: Важно помнить о том, что вы сохраняете последний загруженный документ для использования при загрузке следующей страницы. Убедитесь, что вы правильно обновляете переменную
lastDocument
в каждой функции. -
Корректная работа с сортировкой: При запросах на получение предыдущих страниц вам необходимо установить сортировку в
descending: true
, чтобы получать документы с более старыми временными метками, так как вы хотите навигировать к более ранним записям. -
Проверка
items.count
: Перед загрузкой новой страницы, проверьте, хватает ли у вас элементов для навигации назад:guard items.count > 0 else { return }
Полная реализация loadPreviousPage()
Вот пример того, как может выглядеть обновлённая функция loadPreviousPage
:
func loadPreviousPage() {
guard !isLoading, currentPage > 0 else { return }
isLoading = true
currentPage -= 1
var query = db.collection("items")
.order(by: "timestamp", descending: true)
if let firstDocument = items.first?.document { // Предполагается, что вы храните ссылку на документ
query = query.end(beforeDocument: firstDocument)
.limit(to: itemsPerPage)
}
query.getDocuments { [weak self] snapshot, error in
defer { self?.isLoading = false } // Убедитесь, что isLoading обновлён даже при ошибке
if let error = error {
print("Error getting documents: \(error)")
return
}
guard let snapshot = snapshot, !snapshot.isEmpty else {
return // Нет больше документов
}
self.items = snapshot.documents.compactMap { self.documentToItem($0) }
self.lastDocument = snapshot.documents.last
}
}
Заключение
Надеюсь, предложенные рекомендации помогли вам лучше понять, как реализовать корректную пагинацию в вашем приложении с Firestore. Убедитесь, что вы тестируете каждую функцию отдельно и проверяете, возвращает ли она ожидаемые результаты. Следуйте данным советам, и вы сможете эффективно организовать навигацию между страницами, избегая проблем с повторяющимися или некорректными элементами.