dictionaryswiftui

Generic parameter 'C' could not be inferred


I have a Data class as follows:

    class Data: Hashable, Equatable {
        var id: Int = 0
        var name: String = ""
        var date: Date = Date()
        var paymentStatus: Bool = false
    
        static func == (lhs: Data, rhs: Data) -> Bool {
            return lhs.id ==    
        }
    
        func hash(into hasher: inout Hasher) {
            hasher.combine(id)
        }
    }

    var data1: Data {
        let data = Data()
        data.id = 1
        data.name = "John"
        data.date = Calendar.current.date(byAdding: DateComponents(day: -6), to: Date())!
        data.paymentStatus = true
        return data
    }

var data2: Data {
    let data = Data()
    data.id = 2
    data.name = "Peter"
    data.date = Date()
    data.paymentStatus = false
    return data
}

I’m trying to display the data in sections as follows:

struct ContentView: View {
    
    var data: [Data] = [data1, data2] 
    
    var body: some View {
        List {
            ForEach(groupByDate(data), id: \.self) { studentsInMonth in 
                Section(header:Text(Date(), style: .date)) {
                    ForEach(data, id:\.self) { item in 
                        HStack {
                            Text(item.name)
                            padding()
                            Text(item.date, style: .time)
                            if(item.paymentStatus == false) {
                                Image(systemName: "person.fill.questionmark")
                                    .foregroundColor(Color.red)
                            } else {
                                Image(systemName: "banknote")
                                    .foregroundColor(Color.green)
                            }
                        }
                    } // ForEach ends here... 
                } // section ends here
            } // ForEach ends here
        } // List ends here
    }
}

func groupByDate(_ data: [Data]) -> [Date: [Data]] {
    let empty: [Date: [Data]] = [:]
    return data.reduce(into: empty) { acc, cur in
        let components = Calendar.current.dateComponents([.year, .month], from: cur.date)
        let date = Calendar.current.date(from: components)!
        let existing = acc[date] ?? []
        acc[date] = existing + [cur]
    }
}

Not sure what mistake I’m making, but its throwing two errors:

  1. Cannot convert value of type ‘[Date:[Data]]’ to expected argument type
  2. Generic parameter ‘C’ could not be inferred

Appreciate any help


Solution

  • Right now, you're trying to iterate through a Dictionary using ForEach, which doesn't work -- ForEach expects a RandomAccessCollection, which is most often, an Array.

    Instead, you can iterate over just the keys of the Dictionary.

    struct ContentView: View {
        
        var data: [Data] = [data1, data2]
        
        var body: some View {
            let grouped = groupByDate(data)
            List {
                ForEach(Array(grouped.keys), id: \.self) { key in
                    let studentsInMonth = grouped[key]!
                    Section(header:Text(key, style: .date)) {
                        ForEach(studentsInMonth, id:\.self) { item in
                            HStack {
                                Text(item.name)
                                padding()
                                Text(item.date, style: .time)
                                if(item.paymentStatus == false) {
                                    Image(systemName: "person.fill.questionmark")
                                        .foregroundColor(Color.red)
                                } else {
                                    Image(systemName: "banknote")
                                        .foregroundColor(Color.green)
                                }
                            }
                        } // ForEach ends here...
                    } // section ends here
                } // ForEach ends here
            } // List ends here
        }
    }
    

    Although the above works, I'd consider refactoring your groupByDate function so that it returns an Array directly instead of having to go through the above transformations.

    Also, unrelated to your question, but right now, in your header, you're displaying a date -- you get just the year and month, but then display it as a full date, so the header says "January 1, 2022" for all the January dates. You probably want to refactor this to just show the month and year.