Our next project makes use of the current tech hotness, artificial intelligence – or machine learning as Apple calls it.
The main takeaway from project 4 is how to use a tool called CreateML to make an algorithm that predicts your ideal bedtime when given the input of a preferred wake time and amount of coffee drunk. The process of generating a model seems very simple, but the required input data is pretty massive – a CSV of 10,000 entries – so I’m not sure how likely I am to make use of this kind of technology in real life.
We also learn the basics of date handling – although I know from bitter experience that this is one of the hardest things to deal with in programming so this project is literally just scraping the surface. We also learn about another input control called Stepper
.
Challenge
As usual, we have 3 challenges to complete to extend or modify the app.
Replace VStack
with Section
This isn’t much of a challenge, more of style preference. In any case it’s a relatively simple change. I comment out the VStack
and add in a Section
instead.
//VStack(alignment: .leading, spacing: 0) {
//Text("When do you want to wake up?")
//.font(.headline)
Section("When do you want to wake up?") {
I think I prefer the look of the Section
but, for the time picker especially, neither that nor the original VStack
layout looks great to my eye.
Replace a Stepper
with a Picker
This throws up an interesting little snag. Initially my code has an off-by-one error when using the picker, so I pick 1 cup but the calculation is done with 2. I then modify the label to be x-1 cups but that then gives me a 0 cups option. I haven’t yet solved this but I may revisit it when I have some time.
Picker("Number of cups", selection: $coffeeAmount) {
ForEach(1..<21) {
Text("^[\($0 - 1) cup](inflect: true)")
}
}
Always display the bedtime
I comment out all the alert
stuff and modify calculateBedtime
to return a Date
. After that it’s just a case of making a Text
view that calls the function and formats the output.
// New section
Section("Your ideal bedtime is...") {
Text(calculateBedtime().formatted(date: .omitted, time: .shortened))
.font(.title.bold())
}
// New calculateBedtime
func calculateBedtime() -> Date {
do {
let config = MLModelConfiguration()
let model = try SleepCalculator(configuration: config)
let components = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
let hour = (components.hour ?? 0) * 60 * 60
let minute = (components.minute ?? 0) * 60
let prediction = try model.prediction(wake: Int64((hour + minute)), estimatedSleep: sleepAmount, coffee: Int64(coffeeAmount))
let sleepTime = wakeUp - prediction.actualSleep
return sleepTime
} catch {
}
return Date.now
}
I feel like return Date.now
should maybe be within the catch
block, but I’m not 100% sure. I might go back and have a look at that.
Result
