Firestore 在不重新加载应用程序的情况下不更新 SwiftUI 网格

Posted

技术标签:

【中文标题】Firestore 在不重新加载应用程序的情况下不更新 SwiftUI 网格【英文标题】:Firestore not updating SwiftUI grid without reloading the app 【发布时间】:2020-08-24 15:37:33 【问题描述】:

我按照在线教程进行操作,似乎有一些工作正常,但是我正在尝试根据有效的订单自动更新我的视图,但前提是我关闭并重新打开应用程序。

Observer.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
                        
                    
                
            
        
    

Home.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)
                    
                
            
        
    

postCard.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 是异步的——即。在后台运行。

无论您在哪里更新 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)

【讨论】:

将代码包装在 DispatchQueue.main 中似乎运行没有错误,但似乎仍然没有更新网格,我添加了 postCard.swift 以防我在那里做错了什么 @zeem 您是否检查(打印、调试)您的异步 firebase 函数是否按预期返回所有内容? 它似乎运行正常,只是滚动视图/LazyVGrid 似乎没有在不重新加载应用程序的情况下更新订单。 感谢@pawello2222 进行了更改,但是不确定在哪里调用我在 else 之后添加为 .onAppear 语句的 postobserved.loadPosts() 但认为它只是循环并得到错误“每个布局项可能只发生一次..” ForEach(postsobserved.posts.sorted(按条件,似乎不喜欢添加“喜欢”时似乎只是崩溃了。我有一种感觉我在某处做一些非常愚蠢的事情 有时你只需要一杯更浓的咖啡 :) 非常感谢你的帮助 @pawello2222 ForEach(postsobserved.posts.sorted(by: $0.likes > $1.likes ))I in ... 解决了它现在应该工作的问题

以上是关于Firestore 在不重新加载应用程序的情况下不更新 SwiftUI 网格的主要内容,如果未能解决你的问题,请参考以下文章

System.load() 方法在不设置 LD_LIBRARY_PATH 环境变量的情况下不加载共享库

我们可以在不重新设置整个适配器的情况下更新 Firestore 回收器适配器的查询吗?

jquery 数据表头在某些情况下不扩展或扩展缓慢,例如重新加载

如何在不重新加载页面的情况下重新启动 Angular 应用程序?

在单页应用程序中,当在 VueJs 中输入新数据时,如何在不重新加载页面的情况下重新加载 api [关闭]

Android WebView - 在不重新加载的情况下设置 HTML