I have this program with SwiftUI. The program is for calculating the bedtime using machine learning based on 3 user inputs. I have a Text("") showing users their updated bedtime.
I want the program to update the bedtime automatically and display it on my Text(""). I tried many methods and none seems to work. What I tried so far
Does anyone know if there's an easier way to have the bedtime updated however the user scrolls the picker and whenever a variable changes?
struct ContentView: View {
static var defaultWakeUpTime : Date {
var defaultTime = DateComponents()
defaultTime.hour = 7
defaultTime.minute = 0
return Calendar.current.date(from: defaultTime) ?? Date()
}
@State private var wakeUp = defaultWakeUpTime
@State private var sleepAmount = 8.0
@State private var coffeeAmount = 0 {
didSet {
calculateSleepTime()
}
}
@State private var showTime : String = " "
func calculateSleepTime() {**CONTENT**}
var body: some View {
NavigationView {
VStack {
Spacer(minLength: 20)
Text("Your optimum sleep time is \(showTime)")
Spacer(minLength: 10)
Section {
Text("When do you want to wake up?")
.font(.headline)
DatePicker("Please choose a time", selection: $wakeUp, displayedComponents: .hourAndMinute)
.labelsHidden()
.datePickerStyle(WheelDatePickerStyle())
}
Spacer()
Form {
Text("How many hours would you like to sleep?")
.font(.headline)
Stepper(value: $sleepAmount, in: 4...12, step: 0.25) {
Text("\(sleepAmount, specifier: "%g" ) hours")
}
}
Spacer()
Section {
Text("How many cups of coffee do you drink?")
.font(.headline)
Picker("Coffee Selector", selection: $coffeeAmount) {
ForEach (1..<21) {
Text("\($0) " + "Cup")
}
}
.labelsHidden()
}
}
.navigationBarTitle(Text("BetterSleep"))
.onAppear(perform: calculateSleepTime)
}
}
}
I would use a viewModel and use subscriptions to track values and calculate sleep time.
Change your ContentView at the top to this
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
Now precede any variables with viewModel.
Create a new .swift file I just called it ViewModel but you don't have to.
import Combine
final class ViewModel: ObservableObject {
@Published private(set) var bedTime: String = ""
@Published var wakeUp: Date = Date()
@Published var sleepAmount: Double = 8.0
@Published var coffeeAmount = 0
private var cancellables = Set<AnyCancellable>()
init() {
$wakeUp
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.calculateSleepTime()
}.store(in: &cancellables)
$sleepAmount
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.calculateSleepTime()
}.store(in: &cancellables)
$coffeeAmount
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.calculateSleepTime()
}.store(in: &cancellables)
}
private func calculateSleepTime() {
// Your Logic
self.bedTime =
}
}
Now anytime one of the values changes the suggested bedtime will update. Remember to add one to the coffeeAmount as it starts at 0.