3
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?

2
  • Is postsData defined as: @ObservedObject let postsData: SocialObservable? - it needs to be an @ObservedObject for a view to refresh due to updates. Btw, if you're changing a @Published property, you don't need to also use objectWillChange.send Commented Aug 17, 2020 at 2:38
  • Hmm I changed it to @ObservedObject var postsData = SocialObservable(userInfo: UserData()), but it's still the same problem... For that objectWillChange.send, sure I can change it to a @published property - I just want the view to be able to toggle newFetch. Commented Aug 17, 2020 at 3:27

1 Answer 1

1

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)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.