I'm trying to recreate a view that matches the event list in the stock iOS Calendar app. I have the following code which generates a list of events with each event separated into its own section by Date:
var body: some View {
NavigationView {
List {
ForEach(userData.occurrences) { occurrence in
Section(header: Text("(occurrence.start, formatter: Self.dateFormatter)")) {
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
}
}
.navigationBarTitle(Text("Events"))
}.onAppear(perform: populate)
}
The problem with this code is that if there are two events on the same date, they are separated into different sections with the same title instead of being grouped together into the same section.
As a Swift novice, my instinct is to do something like this:
ForEach(userData.occurrences) { occurrence in
if occurrence.start != self.date {
Section(header: Text("(occurrence.start, formatter: Self.dateFormatter)")) {
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
} else {
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
self.date = occurrence.start
But in Swift, this gives me the error "Unable to infer complex closure return type; add explicit type to disambiguate" because I'm calling arbitrary code (self.date = occurrence.start) inside ForEach{}, which isn't allowed.
What's the correct way to implement this? Is there a more dynamic way to execute this, or do I need to abstract the code outside of ForEach{} somehow?
Edit: The Occurrence object looks like this:
struct Occurrence: Hashable, Codable, Identifiable {
var id: Int
var title: String
var description: String
var location: String
var start: Date
var end: String
var cancelled: Bool
var public_occurrence: Bool
var created: String
var last_updated: String
private enum CodingKeys : String, CodingKey {
case id, title, description, location, start, end, cancelled, public_occurrence = "public", created, last_updated
}
}
Update:
The following code got me a dictionary which contains arrays of occurrences keyed by the same date:
let myDict = Dictionary( grouping: value ?? [], by: { occurrence -> String in
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
return dateFormatter.string(from: occurrence.start)
})
self.userData.latestOccurrences = myDict
However, if I try and use this in my View as follows:
ForEach(self.occurrencesByDate) { occurrenceSameDate in
// Section(header: Text("(occurrenceSameDate[0].start, formatter: Self.dateFormatter)")) {
ForEach(occurrenceSameDate, id: occurrenceSameDate.id){ occurrence in
NavigationLink(
destination: OccurrenceDetail(occurrence: occurrence)
.environmentObject(self.userData)
) {
OccurrenceRow(occurrence: occurrence)
}
}
// }
}
(Section stuff commented out while I get the main bit working)
I get this error: Cannot convert value of type '_.Element' to expected argument type 'Occurrence'
See Question&Answers more detail:
os