I have a project where I want to display badges (an image with a title) in a horizontal scroll view. The data for the badges is stored in a Firestore database. My code can fetch the data and show the badges in a horizontal scroll view. At the moment the badges are in a random order and I want to be able to sort them so they are in the correct order. For example:
Currently I have 4 badges, These include:
10k steps badge, 20k steps badge, 30k steps badge, 40k steps badge
This is the order I want them to be displayed in the horizontal scrollview however they are in a random order.
How can I order this data correctly? I am currently using a ForEach to Loop through the data.
My Model for the badges is as follows:
struct FitnessBadge: Identifiable {
var id: String
var fitness_badge_name: String
var fitness_badge_details: String
var fitness_badge_image: String
}
The View for the Badges is as follows:
struct FitnessBadgeView: View {
var fitnessBadge: FitnessBadge
var body: some View {
VStack(alignment: .center){
// Downloading Image From Web...
WebImage(url: URL(string: fitnessBadge.fitness_badge_image))
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 150, height: 150)
.cornerRadius(15)
HStack(spacing: 8){
Text(fitnessBadge.fitness_badge_name)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.black)
}
HStack(alignment: .center){
Text(fitnessBadge.fitness_badge_details)
.font(.subheadline)
.foregroundColor(.gray)
.lineLimit(2)
Spacer(minLength: 0)
}
}
}
}
How the badges are being displayed in a scroll view is as follows:
ScrollView(.horizontal, showsIndicators: false, content: {
VStack(alignment: .leading) {
Text("Fitness Badges")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.black)
HStack(spacing: 10){
ForEach(AchievementPageModel.filteredFitnessBadges){fitnessBadge in
// Badge View...
ZStack(alignment: Alignment(horizontal: .center, vertical: .top), content: {
FitnessBadgeView(fitnessBadge: fitnessBadge)
})
}
}
}
})
I want it to go: 10k steps, 20k steps, 30k steps etc.
The AchievementPageModel.filteredFitnessBadges array is as follows:
class AchievementPageViewModel: NSObject, ObservableObject {
// Fitness Badges Data...
@Published var fitnessBadges: [FitnessBadge] = []
@Published var filteredFitnessBadges: [FitnessBadge] = []
}
And the fetching data for the badges is as follows:
func fetchFitnessBadgeData(){
let db = Firestore.firestore()
db.collection("FitnessBadges").getDocuments { (snap, err) in
guard let fitnessBadgeData = snap else{return}
self.fitnessBadges = fitnessBadgeData.documents.compactMap({ (doc) -> FitnessBadge? in
let id = doc.documentID
let name = doc.get("fitness_badge_name") as! String
let image = doc.get("fitness_badge_image") as! String
let details = doc.get("fitness_badge_details") as! String
return FitnessBadge(id: id, fitness_badge_name: name, fitness_badge_details: details, fitness_badge_image: image)
})
self.filteredFitnessBadges = self.fitnessBadges
}
}
2
Answers
In the view where badges are being displayed, replace:
with:
This will sort by the field
fitness_badge_name
. I have assumed that’s the field that contains "10k", "20k"… if the key is another field, just replace the variable in the.sorted()
function.There is a terrific answer by @HunterLion but it could become impractical and not scaleable because it assumes you’re loading ALL of the documents from
FitnessBadges
into memory (an array) to allow for sorting.What if there are 1 million documents? That could be a LOT of data and overwhelm the device.
Another option is to let Firestore do the heavy lifting; instead of sorting on your app, let Firestore do it!
With larger amounts of data, you will be way better off allowing Firebase to sort on the server and present that sorted data to your app because then you can read smaller ‘chunks’ of data through pagination.
A better solution would be to use
order(by:
like thisThat sorts millions of documents in a flash on the server and then you can get just the section you want – records 1-10 for example, and then 11-20 and they will be sorted.
For reference: Firestore Pagination