TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Grouped list, but the category is in the view model

Forums > SwiftUI

Sorry if I'm repeating a settled topic here, but I'm not sure the right way to phrase this question to get the desired answer.

I want to create a grouped list. Each group should be separated by var category = String.

Here's my data model:

struct MindfulModel: Codable, Identifiable {
    var id = UUID().uuidString
    var description: String
    var image: String
    var name: String
    var audio: String
    var category: String
    var attribution: String
    var link: String
  }

Here's my list code:

ForEach(self.mindfulModel.mindfulList, id:\.category) { category in
                        VStack(alignment: .leading) {
                            Section(header: Text(category.category)) {
                                HStack {
                                    ForEach(self.mindfulModel.mindfulList) { item in
                                        NavigationLink {
                                            AudioView(item: item)
                                        } label: {
                                            MindfulTile(item: item)
                                        }
                                        .scrollTransition { effect, phase in
                                            effect
                                                .scaleEffect(phase.isIdentity ? 1 : 0.8)
                                                .opacity(phase.isIdentity ? 1 : 0.8)
                                        }
                                    }
                                }
                            }
                        }
                    }
                    .onAppear(){
                        self.mindfulModel.meditate()
                    }
class MindfulnessViewModel: ObservableObject  {

    @Published var mindfulList = [MindfulModel]()

    init() {
        meditate()
    }

    func meditate() {

        // get reference from firebase
        let mindfulDb = Firestore.firestore()

        // read the docs at a specific path
        mindfulDb.collection("mindfulness").getDocuments { snapshot, error in

            // check for errors
            if error == nil{
                // no errrors, yay!

                if let snapshot = snapshot {

                    // update the mindfulList
                    DispatchQueue.main.async {
                        // get all the documents in mindfulness
                        self.mindfulList = snapshot.documents.map { mindfulDoc in
                            // create a mindful for each document returned
                            return MindfulModel(id: mindfulDoc.documentID,
                                                description: mindfulDoc["description"] as? String ?? "",
                                                image: mindfulDoc["image"] as? String ?? "",
                                                name: mindfulDoc["name"] as? String ?? "",
                                                audio: mindfulDoc["audio"] as? String ?? "",
                                                category: mindfulDoc["category"] as? String ?? "",
                                                attribution: mindfulDoc["attribution"] as? String ?? "",
                                                link: mindfulDoc["link"] as? String ?? "",
                                                time: mindfulDoc["time"] as? String ?? ""
                                               )
                                               }
                    }
                }
            }
            else {
                // handle the error
            }
        }
    }
}

extension MindfulModel {
    init(name: String, description: String, image: String ,audio: String, category: String, attribution: String, link: String) {
        self.name = name
        self.description = description
        self.image = image
        self.audio = audio
        self.category = category
        self.attribution = attribution
        self.link = link
        self.time = time
    }
}

As you can see, I'm able to put the category at the top of the side-scrolling list... But SwiftUI makes a discrete list for each instance of "category"; I only want one category line for each category.

Please help!

2      

Would be useful if you could share details on this object self.mindfulModel.mindfulList. Is it array? Provide more information about it.

2      

Information added as requested.

2      

hi,

there was a related discussion of this just last week, so see this thread. (it was in the HWS Plus forum, but as a trial HWS+ member, you might still be able to see it?)

hope that helps,

DMG

2      

I suppose one of the ways to handle this is to prepare data for such view, something like this. As to my understanding with your current model you cannot handle sections properly.

struct ContentView: View {
    // We initialize our model
    @StateObject private var vm = ItemModel()

    var body: some View {
        List {
            // We create our section
            ForEach(vm.dictOfItems, id: \.key) { key, value in
                Section(header: Text(key)) {
                    // we create our items
                    ForEach(value) { item in
                        Text(item.name)
                    }
                }
            }
        }
    }
}

struct ModelItem: Identifiable  {
    var id = UUID().uuidString
    var name: String
    var category: String

    // This is just mock data
    static var mockData = [
        ModelItem(name: "One", category: "B"),
        ModelItem(name: "Two", category: "B"),
        ModelItem(name: "Three", category: "C"),
        ModelItem(name: "Four", category: "D"),
        ModelItem(name: "Five", category: "C"),
        ModelItem(name: "Six", category: "A"),
        ModelItem(name: "Seven", category: "A"),
        ModelItem(name: "Eight", category: "B"),
        ModelItem(name: "Nine", category: "D"),
    ]
}

class ItemModel: ObservableObject {
    @Published var arrayOfItems = [ModelItem]()

    // MAIN PART IS TO PREPARE YOU DATA FOR VIEW
    // As this property is computed, as soon as @Published "publishes" change this property recalculated again
    var dictOfItems: [(key: String, value: [ModelItem])] {
        // as soon as you have data in arrayOfItems you convert to data which can be used in grouped list
        // Here we convert fetched array into dictionary of items sorted by category
        // in the format -- [(key: String, value: [ModelItemOne])] e.g [(key: "A", value: ModelItem(name: "Six", category: "A")]
        Dictionary(grouping: arrayOfItems, by: { $0.category }).sorted(by: { $0.key < $1.key })
    }

    init() {
        fetchData()
    }

    // You fetch your data from the sever and assign to @Published var
    // We simulate here for data to arrive in 3 sec
    func fetchData() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.arrayOfItems = ModelItem.mockData
        }
    }
}

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.