#swift #google-cloud-firestore #swiftui
#swift #google-облако-firestore #swiftui
Вопрос:
Я следил за онлайн-руководством и, похоже, у меня есть несколько вещей, которые работают правильно, однако я пытаюсь автоматически обновлять свое представление на основе порядка, который работает, но только если я закрываю и снова открываю приложение.
Наблюдатель.swift
class postsobserver: ObservableObject {
@Published var posts = [datatype1]()
init() {
let settings = FirestoreSettings()
settings.isPersistenceEnabled = true
let db = Firestore.firestore()
db.settings = settings
db.collection("posts").order(by: "likes", descending: true).addSnapshotListener { snap, err in
if err != nil {
print((err?.localizedDescription)!)
return
}
for i in snap!.documentChanges {
if i.type == .added {
let id = i.document.documentID
let name = i.document.get("name") as! String
let image = i.document.get("image") as! String
let likes = i.document.get("likes") as! String
self.posts.append(datatype1(id: id, name: name, image: image, likes: likes))
}
if i.type == .removed {
let id = i.document.documentID
for j in 0 ..< self.posts.count {
if self.posts[j].id == id {
self.posts.remove(at: j)
return
}
}
}
if i.type == .modified {
let id = i.document.documentID
let likes = i.document.get("likes") as! String
for j in 0 ..< self.posts.count {
if self.posts[j].id == id {
self.posts[j].likes = likes
return
}
}
}
}
}
}
}
Главная страница.swift
@ObservedObject var postsobserved = postsobserver()
let columns = [
GridItem(.adaptive(minimum: 100)),
]
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack {
if postsobserved.posts.isEmpty {
Text("no new posts").fontWeight(.heavy)
} else {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(postsobserved.posts) { i in
postCard(user: i.name, image: i.image, id: i.id, likes: i.likes)
}
}
}
}
}
}
Открытка.swift
import SwiftUI
import SDWebImageSwiftUI
import Firebase
import FirebaseFirestore
struct postCard: View {
var user = ""
var image = ""
var id = ""
var likes = ""
var body: some View {
VStack(alignment: .leading) {
HStack {
Text(user)
Spacer()
}
AnimatedImage(url: URL(string: image))
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 100)
HStack {
Button(action: {
let db = Firestore.firestore()
let like = Int.init(self.likes)!
db.collection("posts").document(self.id).updateData(["likes": "(like 1)"]) { err in
if err != nil {
print(err)
return
}
print("updated...")
}
}) {
Image(systemName: "arrow.up.square")
.frame(width: 26, height: 26)
}
Button(action: {
let db = Firestore.firestore()
let like = Int.init(self.likes)!
db.collection("posts").document(self.id).updateData(["likes": "(like - 1)"]) { err in
if err != nil {
print(err)
return
}
print("updated...")
}
}) {
Image(systemName: "arrow.down.square")
.frame(width: 26, height: 26)
}
Spacer()
}.padding(.top, 8)
Text("(likes) Likes")
}.padding(8)
}
}
Я почти уверен, что где-то допустил ошибку, но последние 4 часа пытался кое-что предпринять, и подумал, что пришло время обратиться за помощью
Редактировать:
Когда я нажимаю стрелки, они, похоже, обновляются корректно без перезагрузки приложения, но .order(by: "likes", descending: true)
, похоже, порядок не меняется, пока я не перезагружу приложение.
Ответ №1:
Вам необходимо обновить ваши @Published
свойства в главном потоке.
Похоже, что db.collection("posts").order(by: "likes", descending: true).addSnapshotListener
это асинхронный ie. выполняется в фоновом режиме.
Где бы вы ни обновляли свою posts
переменную:
self.posts.append(datatype1(id: id, name: name, image: image, likes: likes))
или
self.posts.remove(at: j)
и т.д…
оберните это в DispatchQueue.main
:
DispatchQueue.main.async {
self.posts.append(datatype1(id: id, name: name, image: image, likes: likes))
}
Вы также можете обернуть весь свой код в асинхронный ответ в DispatchQueue.main
:
db.collection("posts").order(by: "likes", descending: true).addSnapshotListener { (snap, err) in
DispatchQueue.main.async {
...
}
}
Редактировать
Похоже, вы перезагружаете свои сообщения только в init
. Вот почему это правильно только в начале (когда вы открываете приложение). Если вы хотите перезагрузить их при каком-либо действии, попробуйте следующее:
class postsobserver: ObservableObject {
@Published var posts = [datatype1]()
private let settings = FirestoreSettings()
private let db = Firestore.firestore()
init() {
settings.isPersistenceEnabled = true
db.settings = settings
loadPosts()
}
// function to reload your posts (in the correct order)
func loadPosts() {
db.collection("posts").order(by: "likes", descending: true).addSnapshotListener { snap, err in
DispatchQueue.main.async {
// ...
}
}
}
}
а затем вызывайте его из своего представления всякий раз, когда вы хотите загрузить обновленные записи:
postsobserved.loadPosts()
Кроме того, вместо того, чтобы каждый раз перезагружать записи, вы можете сортировать их локально:
ForEach(postsobserved.posts.sorted(by: { /* condition */ })) {i in ...
Примечание: Я также рекомендую вам переместить вашу логику Firebase в ObservableObject.
Действие этой кнопки:
Button(action: {
let db = Firestore.firestore()
let like = Int(self.likes)!
db.collection("posts").document(self.id).updateData(["likes": "(like - 1)"]) { err in
if err != nil {
print(err)
return
}
print("updated...")
}
}) {
Image(systemName: "arrow.down.square")
.frame(width: 26, height: 26)
}
может быть извлечен в какую-либо функцию внутри вашего postsobserver
и вызван в Button
напрямую:
Button(action: {
postsobserver.updateLikes(...)
}) {
Image(systemName: "arrow.down.square")
.frame(width: 26, height: 26)
}
Комментарии:
1. завернул код в DispatchQueue. кажется, что main запускается без ошибок, но, похоже, все еще обновляет сетку, я добавил postCard.swift, возможно, я что-то делаю не так
2. @zeem Вы проверяли (печатали, отлаживали), что ваша асинхронная функция firebase возвращает все, как ожидалось?
3. кажется, что он работает правильно, просто scrollview / LazyVGrid, похоже, не обновляет порядок без перезагрузки приложения.
4. спасибо, @pawello2222 внес изменения, однако не уверен, где вызывать postsobserved.loadPosts(), который я добавил как оператор .onAppear после else, но думаю, что это просто зациклилось и выдало ошибку «каждый элемент макета может встречаться только один раз ..» ForEach(postsobserved.posts.sorted (по условию, похоже, не нравится, когда добавление «лайков» просто завершается сбоем. У меня такое чувство, что я где-то делаю что-то очень глупое
5. иногда все, что вам нужно, это кофе покрепче 🙂 огромное спасибо за всю вашу помощь @pawello2222 ForEach(postsobserved.posts.sorted(by: {0.лайков > 1.лайков })) {Я в… решил, что теперь все работает так, как должно