Hi, this is my first post and I first wanted to thank Paul and everyone for creating and maintaining such a wonderful community!
(Also, please let me know if I'm breaking any code of conduct or any kind of improvements on how I formatted my question)
I was tackling Day 28's challenge problems. And, here's what I've successfully done so far,
UI Changes
-
Replaced VStack with Sections.
-
Got rid of the Toolbar and Alert - replaced it with a "Calculate" button inside form, where clicking on "Calculate" will display sleep time as a text value (sleepTimeDisplay).
Added methods
-
Added a resetForm function, which is called when the user clicks on "Reset". It wipes all user inputs and restores default values.
-
Added a resetSleepTimeDisplay function, which resets sleepTimeDisplay. In my code, I called it whenever there's a change in user input, to encourage users to click on "calculate" with every new input.
Room for Improvements?
I think that's all the features I wanted to build for this project, and it's working as intended. But I do think there are many improvements that can be made in the code.
I am definitely not too happy about how I used three individual onChange() to call resetSleepTimeDisplay for every section. I did read up on how to pass multiple parameters on onChange, but I couldn't successfully write the code. It would be really helpful to find a way to call resetSleepTimeDisplay with just one line of code, instead of calling it with onChange for every section! Thank you!
struct ContentView: View {
@State private var sleepAmount = 8.0
@State private var coffeeAmount = 0
@State private var wakeUp = defaultWakeTime
@State private var sleepTimeDisplay = ""
static var defaultWakeTime: Date {
var components = DateComponents()
components.hour = 7
components.minute = 0
return Calendar.current.date(from: components) ?? Date.now
}
var body: some View {
NavigationView {
Form {
Section {
Picker("How much coffee did you drink", selection: $coffeeAmount) {
ForEach(1..<6) {number in
Text(number == 1 ? "1 cup" : "\(number) cups")
}
}
.pickerStyle(.segmented)
} header: {
Text("How much coffee did you drink?")
.font(.subheadline)
}
.onChange(of: coffeeAmount) {newValue in
resetSleepTimeDisplay()
}
Section {
Stepper("\(sleepAmount.formatted()) hours", value: $sleepAmount, in: 0...24, step: 0.5) {_ in
}
} header: {
Text("How much sleep do you want?")
.font(.subheadline)
}
.onChange(of: sleepAmount) {newValue in
resetSleepTimeDisplay()
}
Section {
HStack {
Text("I'll wake up at")
DatePicker("Please enter a time", selection: $wakeUp, displayedComponents: .hourAndMinute)
.labelsHidden()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .trailing)
}
} header: {
Text("When do you want to wake up?")
.font(.subheadline)
}
.onChange(of: wakeUp) {newValue in
resetSleepTimeDisplay()
}
Button("Calculate", action: calculateBedtime)
Text(sleepTimeDisplay == "" ? "Tap Calculate to get your new bedtime." : "Your ideal bedtime is \(sleepTimeDisplay)")
Button("Reset") {
resetForm()
resetSleepTimeDisplay()
}
}
.navigationTitle("BetterRest")
}
}
func calculateBedtime() {
do {
let config = MLModelConfiguration()
let model = try SleepCalculator(configuration: config)
let sleepTimeComponent = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
let hour = (sleepTimeComponent.hour ?? 0) * 3600 // in seconds
let minute = sleepTimeComponent.minute ?? 0 * 60 // in seconds
let prediction = try model.prediction(wake: Double(hour + minute), estimatedSleep: sleepAmount, coffee: Double(coffeeAmount))
let sleepTime = wakeUp - prediction.actualSleep
sleepTimeDisplay = "\(sleepTime.formatted(date: .omitted, time: .shortened))"
} catch {
sleepTimeDisplay = "Sorry, there was an error calculating your bedtime."
}
}
func resetForm() {
sleepAmount = 8
coffeeAmount = 0
wakeUp = ContentView.defaultWakeTime
}
func resetSleepTimeDisplay() {
sleepTimeDisplay = ""
}
}