UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

day 28 BetterSleep project

Forums > 100 Days of SwiftUI

Hi everyone,

I am on day 28 now and just finished typing up the project. Now I have a question for the challenge section from project 4 wrap up.

it says I have to remove the button to calculate bedtime, therefore in order to show the bedtime, a function needs to be refreshed every time when a picker // stepper gets changed. and i can't seem to find a modifier that can do exactly that.

3 of the closest I found were

  1. onAppear - which only seem to work when the page gets refreshed
  2. onReceived - I dont know how to work the publisher
  3. onTapGesture works on a count of 1, but if the user scrolls to the right time and cup of coffees, it doesn't register as a tap, also, tapping the stepper after adding this modifier doesn't actually do any increment // decrement on the stepper. can someone please help.
import SwiftUI
//in CoreML data, the wakeUp time is the seconds pass from 12am
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
    @State private var showTime = ""
    @State private var timeToSleep = ""

    func calculateSleepTime() -> String {

        let model = sleepCalculator()
        let timeComponents = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
        let hour = (timeComponents.hour ?? 0) * 60 * 60
        let minute = (timeComponents.minute ?? 0) * 60
        let formatter = DateFormatter()
        formatter.timeStyle = .short

        do {
            let prediction1 = try model.prediction(wake: Double(hour + minute), estimatedSleep: sleepAmount, coffee: Double(coffeeAmount + 1))
            let sleepTime = wakeUp - prediction1.actualSleep
            showTime = formatter.string(from: sleepTime)
            return showTime
        }
        catch {
            return "ERROR"
        }
    }

    func addingTimeToSleep() {
        timeToSleep = calculateSleepTime()
    }

    var body: some View {
        NavigationView {
                VStack {
                    Spacer(minLength: 20)

                    Text("Your optimum sleep time is \(timeToSleep)")

                    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"))
                .onTapGesture(perform: addingTimeToSleep) //*My ontapgesture*
        }
    }
}

3      

Hi Friends I have the same problem and i tried to solve this with Timer but i could do it because i dont know how can i use Selector ( @obj dont work).

3      

I don't understand your question. Each time one @State variable is changed in you view, your view is refreshed. During the refresh the line

                    Text("Your optimum sleep time is \(timeToSleep)")

is calculated so your view should alway show the good timeToSleep. There is no need to find a specific modifier. Doesn't the timeToSleep change when you change the wake up time, the time to sleep or the volume of coffee?

3      

hey sorry, only just saw your reply. the code I posted doesn't automatically update the ideal sleep time in Text. When I did this project, I hadn't learned about @ObservableOject, @published and @observedObject. It may work this way but I learned another way to do it as well which was way harder and I am still buffled how its done. But the solution is this in case you're interested.

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 = 
    }
}

3      

You can make your original code work with these changes:

Remove: @State private var showTime = ""

Change:

showTime = formatter.string(from: sleepTime)
return showTime

to:

return formatter.string(from: sleepTime)

Change:

Text("Your optimum sleep time is \(timeToSleep)")

to:

Text("Your optimum sleep time is \(calculateSleepTime())")

(And you don't need the addingTimeToSleep() function and the onTapGesture either but it won't hurt anything to leave them in.)

The issue was that your calculateSleepTime() function was never being called, which you can verify by placing a breakpoint in that function.

6      

Thank you. that's a much easier solution!!

3      

@roosterboy Great solution! Thanks. helped me very much

3      

OH my god ,you are so powerful,thank you ,roosterboy! Your solution is right,but I still don't understand the reason.

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.