I'm not sure why I am getting this error: Type '()' cannot conform to 'View'
After looking up the error, I understand that xcode is saying that my code is not returning a view. However, my code is returning a view. So I am having trouble understanding why I am getting the error...
Please see below code where the error occurs:
/// A list to display time logs
struct TimeLogsList: View {
/// The view model required to generate this view with data
@State var model = TimeLogsListViewModel(timeLogs: MockData.sharedMockData.timeLogs)
var body: some View {
// Generating a List view with timeLogs sectioned by date from the view model
List() {
// Looping through each date in sections
model.sections.forEach { date in
// Setting each Section Title as the date in the current loop
Section(header: Text("\(date)")) {
// Looping through each timeLog in timeLogs
$model.timeLogs.forEach { $timeLog in
// If the timeLog holds the same date as the date in the loop then...
if $timeLog.date == date {
// ...timeLog is added as a new row
TitleWithHoursListRowView(timeLog: $timeLog)
}
} // End of TimeLogs Loop
}
} // End of Sections Loop
}
}
}
Here is the code you will need to reproduce the error (I have included inline comments to make this easy to understand.)
A struct: TimeLogsListViewModel:
/// A model to be passed into a List View of timeLogs sectioned by date
struct TimeLogsListViewModel {
/// An array of timeLog dates to be used as sections for a List View - can only be set via `self.generateSections()`
private(set) var sections = [Date]()
/// The timelogs for the List View
var timeLogs: [TimeLog]
/// Initializes a TimeLogsListViewModel
/// - parameter timeLogs: An array of TimLog
init(timeLogs: [TimeLog]) {
self.timeLogs = timeLogs
generateSections()
}
/// Gets the dates from an array of timeLogs and forms an array of those dates - no dates are duplicated.
mutating func generateSections() {
var sections: [Date] = []
// Looping through each timelog, adding its date to the sections array if not already
self.timeLogs.forEach { timeLog in
if !sections.contains(timeLog.date) {
sections.append(timeLog.date)
}
}
// Sorting the dates in the sections array
sections = sections.sorted(by: { $0.compare($1) == .orderedDescending })
// Updating self.sections to contain an array of sorted dates
self.sections = sections
}
}
A struct: TimeLog:
/// A data struct that holds the data of a single time log
struct TimeLog: Identifiable {
// TimeLog UUID to conform to identifiable
var id = UUID()
/// The date for when the logged data was completed
var date: Date
/// The data the time log contains
var details: Details
/// A data struct modeling the data a time log contains
struct Details {
/// A brief description describing the work that was done
var shortDescription: String
/// A more detailed description describing the work that was done
var longDescription: String?
/// The amount of hours that was spent to complete the work for this log
var hours: Double
}
}
A class: MockData:
/// This singleton holds mock data used for testing
class MockData {
// MARK: Static Properties
/// The shared instance for MockData
static let sharedMockData = MockData()
// MARK: Instance Properties
// Dates
var dateOne: Date
var dateTwo: Date
var dateThree: Date
var dateFour: Date
// TimeLogs
var timeLogOne: TimeLog
var timeLogTwo: TimeLog
var timeLogThree: TimeLog
var timeLogFour: TimeLog
// TimeLog arrays
var timeLogs: [TimeLog]
// MARK: Init
/// A private init making MockData a singleton
private init() {
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yy"
// Initializing dates
dateOne = formatter.date(from: "01/02/23") ?? Date()
dateTwo = formatter.date(from: "01/04/23") ?? Date()
dateThree = formatter.date(from: "02/05/23") ?? Date()
dateFour = formatter.date(from: "04/11/23") ?? Date()
// Initializing timeLogs
timeLogOne = TimeLog(date: dateOne, details: TimeLog.Details(shortDescription: "First Time Log", hours: 10))
timeLogTwo = TimeLog(date: dateTwo, details: TimeLog.Details(shortDescription: "Second Time Log", hours: 8))
timeLogThree = TimeLog(date: dateThree, details: TimeLog.Details(shortDescription: "Third Time Log", hours: 11))
timeLogFour = TimeLog(date: dateFour, details: TimeLog.Details(shortDescription: "Fourth Time Log", hours: 11))
timeLogs = [timeLogOne, timeLogOne, timeLogTwo, timeLogThree, timeLogFour]
}
}
And a View: TitleWithHoursListRowView:
/// A view used for displaying a title and a log of hours - intended to be used in a list row.
struct TitleWithHoursListRowView: View {
@Binding var timeLog: TimeLog
var body: some View {
HStack() {
Text(timeLog.details.shortDescription)
Spacer()
Text("Hours:")
Text("\(Int(timeLog.details.hours))")
}
}
}
Thanks ahead everyone :)
Could you try this:
ForEach(model.sections, id:\.self) { date in
Section(header: Text("\(date)")) {
ForEach($model.timeLogs) { $timeLog in
if timeLog.date == date {
TitleWithHoursListRowView(timeLog: $timeLog)
}
}
}
}
You can change the timeLog
or date
Bindings
as per your need.