guckmalmensch
guckmalmensch

Reputation: 1181

SwiftUI list not updating when the array changes

VStack(spacing: 0){
    List{
        ForEach(postsData.fetchedPosts, id: \.postID) { post in
            SocialPostView(post: post, showAccount: self.$showAccount, fetchedUser: self.$fetchedUser)
                .padding(.vertical)
                .listRowInsets(EdgeInsets())
                .onAppear {
                    self.elementOnAppear(post)
            }
        }
    }
    .pullToRefresh(isShowing: $isShowing) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.isShowing = false
            self.postsData.newFetch = true
            self.postsData.fetchPosts(userInfo: self.userInfo)
        }
    }
}
private func elementOnAppear(_ post: Post) {
    /* onAppear on the view is called when a view appears on screen.
     elementOnAppear asks the view model if the element is the last one.
     If so, we ask the view model to fetch new data. */
    
    if self.postsData.isLastPostInList(post: post) {
        self.postsData.fetchPosts(userInfo: self.userInfo)
    }
}

When each list element appears, it checks if it's the last element in the array. If it is, it fetches more from Firestore and updates fetchedPosts. However, when the array updates, the List is not updated, so no new elements show up.

This is the ObservableObject, which publishes the array.

class SocialObservable: ObservableObject{
    let db = Firestore.firestore()
    
    let objectWillChange = ObservableObjectPublisher()
    
    @Published var fetchedPosts = [Post]()
    
    @Published var lastSnap : DocumentSnapshot?
    @Published var reachedEnd = false
    
    var currentListener: ListenerRegistration?
    
    var newFetch = false {
        willSet{
            objectWillChange.send()
        }
    }
    
    init(userInfo: UserData){
        print(userInfo.uid)
        fetchPosts(userInfo: userInfo)
    }
    
    func fetchPosts(userInfo: UserData){
        var first: Query?
        
        if lastSnap == nil || newFetch {
            //not last snapshot, or just updated feed
            
            if newFetch{
                newFetch.toggle()
                fetchedPosts = [] // clean up if new fetch
            }
            
            first = db.collection("posts")
                .whereField("availability", arrayContains: userInfo.uid)
                .order(by: "date", descending: true)
                .limit(to: 1)
        }
        else {
            first = db.collection("posts")
                .whereField("availability", arrayContains: userInfo.uid)
                .order(by: "date", descending: true)
                .start(afterDocument: lastSnap!)
                .limit(to: 1)
        }

        first?.getDocuments(completion: { (snapshot, error) in
            guard let snapshot = snapshot else {
                print("Error: \(error.debugDescription)")
                return
            }
            let doc = snapshot.documents.map({postFromDB(obj: $0.data(), id: $0.documentID)})
            
            doc.map({print($0.postID)})
            // append to fetched posts
            self.fetchedPosts = self.fetchedPosts + doc
            print(self.fetchedPosts.count)
            
            //prepare for the next fetch
            guard let lastSnapshot = snapshot.documents.last else {
                // the collection is empty. no data fetched
                self.reachedEnd = true
                return
            }
            // save last snapshot
            self.lastSnap = lastSnapshot
        })
        
        
    }
    
    
    
    func isLastPostInList(post: Post) -> Bool {
        return post.postID == fetchedPosts.last?.postID
    }
    
    
}

Is there any workaround for this?

Upvotes: 3

Views: 1314

Answers (1)

Asperi
Asperi

Reputation: 257563

A couple of things

class SocialObservable: ObservableObject{
    let db = Firestore.firestore()
    
    // let objectWillChange = ObservableObjectPublisher()     // << remove this
    
    // ...
    
    var newFetch = false {
        willSet{
            self.objectWillChange.send()    // ObservableObject has default
        }
    }

    // ...

and on update modify published on main queue

    doc.map({print($0.postID)})
    // append to fetched posts
    DispatchQueue.main.async {
       self.fetchedPosts = self.fetchedPosts + doc
    }
    print(self.fetchedPosts.count)

Upvotes: 1

Related Questions