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?
postsDatadefined as:@ObservedObject let postsData: SocialObservable? - it needs to be an@ObservedObjectfor a view to refresh due to updates. Btw, if you're changing a@Publishedproperty, you don't need to also useobjectWillChange.send@ObservedObject var postsData = SocialObservable(userInfo: UserData()), but it's still the same problem... For thatobjectWillChange.send, sure I can change it to a @published property - I just want the view to be able to toggle newFetch.