How to make uicollectionview reload once it receives data from firebase?
You need to call self.collectionView!.reloadData()
after you have retrieved you data and stored it into your DataSource
.
i.e in your completionBlock:
of your Firebase .observeSingleEventOfType
databaseRef.child("users").child(userID!).child("medals").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user medals
self.identities3 = snapshot.value as! [String]
self.collectionView!.reloadData()
})
How should I retrieve data to the UICollectionViewCell from Firebase?
Reading through the comments it appears you are asking how to read data from Firebase. Since there's no Firebase code in the question, I'm having to guess what you ultimately want to accomplish so I would first direct you to the Firebase Guide Reading and Writing Data as that covers the topic. Then, see Working With Lists
To provide a leg up, here's one possible solution.
Since you have a tableView, that would usually be backed by a tableView datasource. So the concept is to read the data from Firebase and then populate that datasource, then refresh the tableView which will show the objects to the user in the UI.
Let's start with a class to hold the data from Firebase Realtime Database
class ProgramClass {
var description = ""
var image = ""
var title = ""
init(withSnapshot: DataSnapshot) {
self.description = withSnapshot.childSnapshot(forPath: "description").value as? String ?? "No Description"
self.image = withSnapshot.childSnapshot(forPath: "image").value as? String ?? "No Image"
self.title = withSnapshot.childSnapshot(forPath: "title").value as? String ?? "No Title"
}
}
and then a class var to hold those objects
var programsArrayDataSource = [ProgramClass]()
keep in mind that class var will act as the dataSource which backs your tableview. When a change occurs in firebase, the dataSource would be updated via Firebase observe (.childAdded, .childChanged or .childRemoved) and then the tableView reloaded.
To actually read the Firebase structure presented in your question, use the following code. This will both read Firebase as well as populating the dataSource for your tableView.
func fetchPrograms() {
let programsRef = self.ref.child("programs")
programsRef.observeSingleEvent(of: .value, with: { snapshot in
let allPrograms = snapshot.children.allObjects as! [DataSnapshot]
for programSnap in allPrograms {
let aProgram = ProgramClass(withSnapshot: programSnap)
self.programsArrayDataSource.append(aProgram)
}
self.programTableView.reloadData()
})
}
From there you can handle the tableView code; when a row needs refreshing, get that object from the dataSource via the index (row), read the data from that object and populate the tableView cell with it in whatever format you need.
Reload TableView data only after downloading everything from the firebase database Swift
You're nesting observers on nested data, which seems like a waste of code. When you attach an observer to a location, all data under that location is loaded already.
So you can just loop over the nested snapshot to get the same result:
newRef.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let userSnapshot = child as! DataSnapshot
let userKey = userSnapshot.key
for child in userSnapshot.children {
let imageSnapshot = child as! DataSnapshot
var imageDownloaded: UIImage?
...
With that out of the way, let's move on to your real question: how can you detect when all images have loaded.
One simple, cross-platform way to do this is by simply counting how many images you have loaded, and compare that to how many images you know exist. Since you have a tree of only existing images, you can do both in a iteration over the double nested structure
let knownImageCount = 0 // we start with no knowledge of any image
let loadedImageCount = 0 // we also haven't loaded any image yet
for child in snapshot.children {
let userSnapshot = child as! DataSnapshot
let userKey = userSnapshot.key
knownImageCount = knownImageCount + userSnapshot.childrenCount // we've found out about N more images
for child in userSnapshot.children {
let imageSnapshot = child as! DataSnapshot
var imageDownloaded: UIImage?
...
URLSession.shared.dataTask(with: url!) { (data, response, error) in
...
loadedImageCount = loadedImageCount + 1 // we loaded an additional image
if loadedImageCount == knownImageCount {
... // we've loaded all known images, we're done!
}
}.resume()
Collection view not reloading after retrieving data
You should always update your UI elements on main thread. No exception here as well. Just execute the reload code on main thread.
dispatch_async(dispatch_get_main_queue(), {
self.mostPopularCollectionView.reloadData()
})
For Swift 3:
DispatchQueue.main.async {
self.mostPopularCollectionView.reloadData()
}
How to reload data after all Firebase calls finished?
If you want to be notified when all the firebase calls have been completed you can use this code
let ref = FIRDatabase.database().reference()
ref.child("users/mchen/groups").observeSingleEvent(of: .value, with: { snapshot in
let groupKeys = snapshot.children.flatMap { $0 as? FIRDataSnapshot }.map { $0.key }
// This group will keep track of the number of blocks still pending
let group = DispatchGroup()
for groupKey in groupKeys {
group.enter()
ref.child("groups").child(groupKey).child("name").observeSingleEvent(of: .value, with: { snapshot in
print("Mary is a member of this group: \(snapshot.value)")
group.leave()
})
}
// We ask to be notified when every block left the group
group.notify(queue: .main) {
print("All callbacks are completed")
}
})
How does it work?
There are 4 main instructions involved.
First of all we create a group
DispatchGroup()
. This value will keep track of the number of pending blocks.let group = DispatchGroup()
Then before starting a new asynchronous call we tell the group there is a new pending block.
group.enter()
Inside the callback closure we tell the group that one block has finished its work.
group.leave()
We tell the block to run a closure when the number of blocks into the group does become zero.
group.notify(queue: .main) {
print("All callbacks are completed")
}
Reload collection view data when user comes back to view controller
The view did load method is only called when the view is instantiated. It appears that it is not being triggered because your transitions are not deinstantiating the collection view cell. (you might be coming back from a pop over, for example)
To force the reload of the data you could try and put that same line of code under view will appear, which is triggered every time the view is about to be presented to the user.
Try doing:
override func viewWillAppear() {
super.viewWillAppear()
self.collectionView.reloadData()
}
The order normally goes, viewDidLoad, viewWillAppear and then viewDidAppear.
the above code should fix it. let me know if it doesn't.
Related Topics
In Swift, Can One Use a String to Access a Struct Property
Why Strings Are Not Equal in My Case
Swift Lazy Stored Property Versus Regular Stored Property When Using Closure
How to Use Spritekit Archives with Skspritenode Subclasses
Swift: How to Fix Infinite Loop When Adding a Value to a Firebase Variable
Why Upload Alamofire Background Request Don't Executes in Background
How How to Perform Multiple Alamofire Requests That Are Finished One After Another
Swift: Dictionaries Inside Array
Swiftui - Show the Data Fetched from Firebase in View
Crash When Running on Device After Second Launch
How, Exactly, Do I Render Metal on a Background Thread
Way to Purge All But One Object Types (Models) in a Realm
Swift Firebase Storage How to Retrieve Image with Unknow Name(Nsuuid)
Fetch Coredata with One to Many Relationship in Swift
How to Use View Controller (Calendarkit) in Swiftui Application
How to Open a Url in Safari Even If My Default Browser Is Not Safari Using Swift in MACos