Project 4 — BetterRest
BetterRest рекомендует время отхода ко сну по желаемому времени подъёма, продолжительности сна и количеству кофе. Мы соединим системные controls с обученной моделью Core ML.
Готовность к проекту
0 из 5 рекомендуемых тем изучено. Проект всегда доступен — это не ограничение.
Рабочий vertical slice
Сначала соберите форму и чистый расчёт. Core ML подключается после того, как этот экран работает.
import SwiftUI
struct ContentView: View {
@State private var wakeUp = defaultWakeTime
@State private var sleepAmount = 8.0
@State private var coffeeAmount = 1
static var defaultWakeTime: Date {
Calendar.current.date(from: DateComponents(hour: 7)) ?? .now
}
private var bedtime: Date {
let coffeePenalty = Double(coffeeAmount - 1) * 10 * 60
let desiredSleep = sleepAmount * 60 * 60 - coffeePenalty
return wakeUp.addingTimeInterval(-desiredSleep)
}
var body: some View {
NavigationStack {
Form {
DatePicker("Проснуться", selection: $wakeUp, displayedComponents: .hourAndMinute)
Stepper("Сон: \(sleepAmount.formatted()) ч", value: $sleepAmount, in: 4...12, step: 0.25)
Stepper("Кофе: \(coffeeAmount)", value: $coffeeAmount, in: 1...20)
Section("Лечь спать") {
Text(bedtime.formatted(date: .omitted, time: .shortened))
.font(.title2.bold())
}
}
.navigationTitle("BetterRest")
}
}
}
#Preview { ContentView() }Этот расчёт намеренно простой и нужен для проверки data flow. Следующий раздел заменяет формулу прогнозом обученной модели.
Состояние формы
@State private var wakeUp = Date.now
@State private var sleepAmount = 8.0
@State private var coffeeAmount = 1DatePicker редактирует Date, Stepper удобно меняет число небольшими шагами. Все controls получают Binding к @State.
DatePicker("Время подъёма", selection: $wakeUp, displayedComponents: .hourAndMinute)
Stepper("Сон: \(sleepAmount.formatted()) часов", value: $sleepAmount, in: 4...12, step: 0.25)Работа с Date
Для интерфейса храни полный Date, а отдельные hour/minute извлекай через Calendar. Так код учитывает календарь пользователя, не занимается ручной арифметикой секунд.
let components = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
let hour = components.hour ?? 0
let minute = components.minute ?? 0Optional здесь обрабатывается через ??: разумный fallback делает вход модели определённым.
Create ML и Core ML
Create ML обучает модель на таблице примеров, а Xcode превращает .mlmodel в типизированный Swift API. Модель нужно создать и добавить в проект один раз.
- Скачай учебный
SleepCalculator.csv. Это небольшой синтетический набор для воспроизводимого упражнения, а не медицинская рекомендация. - В Xcode выбери Xcode → Open Developer Tool → Create ML, затем создай Tabular Regression.
- Укажи CSV как Training Data, выбери
actualSleepкак Target, аwake,estimatedSleepиcoffeeоставь Features. - Нажми Train, открой вкладку Output и сохрани модель под именем
SleepCalculator.mlmodel. - Перетащи файл в Project navigator приложения, включи Copy items if needed и target membership приложения.
- Собери проект один раз. Xcode сгенерирует тип
SleepCalculator; его интерфейс можно увидеть, выбрав модель в Project navigator.
Теперь добавь import CoreML и замени простой расчёт строкой, полученной из модели:
private var modelBedtime: String {
do {
let components = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
let hour = components.hour ?? 0
let minute = components.minute ?? 0
let wakeSeconds = Double(hour * 60 * 60 + minute * 60)
let config = MLModelConfiguration()
let model = try SleepCalculator(configuration: config)
let prediction = try model.prediction(
wake: wakeSeconds,
estimatedSleep: sleepAmount,
coffee: Double(coffeeAmount)
)
let bedtime = wakeUp.addingTimeInterval(-prediction.actualSleep)
return bedtime.formatted(date: .omitted, time: .shortened)
} catch {
return "Не удалось рассчитать время сна"
}
}В Section("Лечь спать") покажи Text(modelBedtime). Ошибка не исчезает в пустом catch: пользователь получает понятное состояние, а приложение остаётся рабочим.
Испытание
Задание
Сделай результат обновляемым прямо в форме без отдельной кнопки и добавь понятное сообщение при ошибке модели.Что уже нужно знать
Computed properties, `do`/`try`/`catch`, optionals и state.Показать подсказку 1
Показать подсказку 2
Показать решение
var bedtime: String {
do {
let components = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
let wakeSeconds = Double((components.hour ?? 0) * 3600 + (components.minute ?? 0) * 60)
let model = try SleepCalculator(configuration: MLModelConfiguration())
let prediction = try model.prediction(
wake: wakeSeconds,
estimatedSleep: sleepAmount,
coffee: Double(coffeeAmount)
)
return wakeUp.addingTimeInterval(-prediction.actualSleep)
.formatted(date: .omitted, time: .shortened)
} catch {
return "Не удалось рассчитать время сна"
}
}Что ты сейчас использовал
Swift Знакомые основы
SwiftUI Новое в проекте
- + @State
- + DatePicker
- + Stepper
- + Core ML
Следом применим Arrays, Strings и validation в игре Word Scramble →.
Комментарии
Вопросы, находки и подсказки по этой странице.
Войди, чтобы задать вопрос или помочь другим.