Duplicate Collectionview Cells After Reloaddata with Firebase Query

Duplicate collectionView cells after reloadData with Firebase query

messages array must be reset before repopulate snapshot document. You can add self.messages.removeAll() before the line for document in snapshot.documents

Why is it every time my tableview reloads, there are duplicates in it?

You are correctly loading data from Firebase.

However, before self.motivationThoughts.append(MdataModel), you need to clear the previous data, otherwise it would just append the fresh data to what you had previously, which I believe explains what you are seeing.

Something like self.motivationThoughts.removeAll() will do it.

Swift showing Firebase duplicating data (.observeChildAdded) not working

You need to clear your model (self.postList) at the beginning of the .observe block like so:

query.observe(.value) { (snapshot) in
self.postList.removeAll() //or however you can clear it
for child in snapshot.children.allObjects as! [DataSnapshot] {
if let value = child.value as? NSDictionary {
let post = Post()
let poster = value["poster"] as? String ?? "Name not found"
let post_content = value["post"] as? String ?? "Content not found"
let post_reveals = value["Reveals"] as? String ?? "Reveals not found"
post.post_words = post_content
post.poster = poster
post.Reveals = post_reveals
post.postID = child.key
self.postList.append(post)
print (post.post_words ?? "none")
DispatchQueue.main.async { self.tableView.reloadData() }
//make this for when child is added but so that it also shows psots already there something like query.observre event type of
}
}

Currently, each time the database is updated with a post, you add all posts to your model once again. Therefore you must clear your model each time you fetch all posts.

The reason why this doesn't work in viewDidLoad is because viewDidLoad is called only once, in the beginning, and not everytime the view appears -- thus the data will not be cleared upon adding a post.

Alternatively, you can use .childAdded -- but then you need to change the way you parse it because each snapshot with .childAdded returns a single post, not all the posts together.

Load CollectionView cells only when all data is loaded

You have asked many things. Yes it is true that the data may not be loaded by the time the view is displayed. Fortunately the UICollectionView was designed with this in mind. I recommend watching the "Let's Build That App" YouTube Channel for good tips on using the UICollectionView.

We do a lot of dynamic searching of a remote server so the content is constantly being updated and may even update as we are loading it. Here is how we handle it.

cellForItemAt is not the time to do a server call. Use this after the data is loaded. In general do not mix your server loading with the data display. Write the UICollectionView with the intent that it is operating on an internal list of items. This will also make your code more readable.

Add your slow server functions in such a way that they update the data quickly and call reloadData() after you have done so. I will provide code below.

In our UICollectionViewController we keep an array of the current content, something like this. We start with these variables up top:

var tasks = [URLSessionDataTask]()
var _entries = [DirectoryEntry]()
var entries: [DirectoryEntry] {
get {
return self._entries
}
set(newEntries) {
DispatchQueue.main.async {
self._entries = newEntries
}
}
}

Everytime we fetch data from the server we do something like:

func loadDataFromServer() {

// Cancel all existing server requests
for task in tasks {
task.cancel()
}
tasks = [URLSessionDataTask]()

// Setup your server session and connection
// { Your Code Here }

let task = URLSession.shared.dataTask(with: subscriptionsURL!.url!, completionHandler: { data, response, error in
if let error = error {
// Process errors
return
}

guard let httpresponse = response as? HTTPURLResponse,
(200...299).contains(httpresponse.statusCode) else {
//print("Problem in response code");
// We need to deal with this.
return
}
// Collect your data and response from the server here populate apiResonse.items
// {Your Code Here}
var entries = [DirectoryEntry]()

for dataitem in apiResponse.items {
let entry = Entry(dataitem)
entries.append(dataitem)
}
self.entries = entries
self.reloadData()
})
task.resume()
tasks.append(task)
}

First, we keep tack of each server request (task), so that when we initiate a new one, we can cancel any outstanding requests, so they don't waste resources and so they don't overwrite the current request. Sometimes an older request can actually finish after a newer request giving you weird results.

Second, we load the data into a local array within the server loading function (loadDataFromServer), so that we don't change the live version of the data as we are still loading it. Otherwise you will get errors because the number of items and content might change during the actual display of the data, and iOS doesn't like this and will crash. Once the data is completely loaded, we then load it into the UIViewController. We made this more elegant by using a setter.

self.entries = entries

Third, you have to instruct UICollectionView that you have changed your data, so you have to call reloadData().

self.reloadData()

Fourth, when you update the data use the Dispatch code because you cannot update a UI from background thread.

How and when you decide to load data from the server is up to you and your UI design. You could make your first call in viewDidLoad().

How do I update collectionview based on added info to firebase

Replace this

ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { (snap) in

with

ref.child("posts").observe(of: .childAdded, with: { (snap) in

to be able to listen to every added post

Retrieving my posts from firebase and display it on my collectionView Cells? ios

To locate specific data within a node a Query is used.

While iterating over the nodes works, if there's a million nodes it's going to take a long time and tie up the UI while waiting for that to happen.

The bottom line is; if you know what you are looking for - in this case a uid - then a query is a super simple solution.

let postsRef = self.ref.child("posts")
let query = postsRef.queryOrdered(byChild: "userID").queryEqual(toValue: "uid_0")
query.observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
for child in snapshot.children { //.value can return more than 1 match
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let myPostURL = dict["urlToImage"] as! String
self.imageURLs.append(myPostURL)
}

} else {
print("no user posts found")
}
}

With the above code, we first create a reference to our posts node. Then a query that will only return this users (uid_0) posts.

We then iterate over the snapshot of posts and add them to the array.

This is much more efficient that loading and evaluating every node in the database.

See Sorting And Filtering data for more information.



Related Topics



Leave a reply



Submit