skip to Main Content

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)
                
                })
            }
        }
    }
})

This is the horizontal scroll view

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


  1. In the view where badges are being displayed, replace:

    ForEach(AchievementPageModel.filteredFitnessBadges)
    

    with:

    ForEach(AchievementPageModel.filteredFitnessBadges.sorted { $0.fitness_badge_name < $1.fitness_badge_name })
    

    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.

    Login or Signup to reply.
  2. 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 this

    db.collection("FitnessBadges").order(by: "fitness_badge_name").getDocuments
    

    That 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search