Вопрос или проблема
Предположим, у меня есть ObservableObject, который живет в SwiftUI App как .environmentObject
final class UserSettings: ObservableObject {
@AppStorage("defaultTaskDuration") var defaultTaskDuration: Int = 1500
//... другие AppStorage
}
(Пользователь может изменить defaultTaskDuration через настройки…)
Как получить к этому доступ в моей модели представления?
@Observable final class TaskDialogViewModel {
var taskDuration = 0
func getDefaultTaskDuration() {
// доступ к defaultTaskDuration
}
}
Что я делаю сейчас: я передаю значение через представление
struct Dialog: View{
@EnvironmentObject var userSettings: UserSettings
@Environment(TaskDialogViewModel.self) private var model
var body: some View {
HStack {
...
}
.onAppear {
model.taskDuration = userSettings.defaultTaskDuration
}
}
}
Это работает. Но мне интересно, есть ли способ без передачи этого в представление?
Ответ или решение
Чтобы передать свойство из класса ObservableObject
в другой объект ObservableObject
в SwiftUI, вы можете использовать систему зависимостей и позволить вашему классу ViewModel извлекать необходимые данные непосредственно из общего состояния вашего приложения, минуя прямую передачу через Views.
В вашем случае, давайте перепишем TaskDialogViewModel
так, чтобы он мог принимать UserSettings
в качестве зависимости. Это можно сделать при создании экземпляра TaskDialogViewModel
. Вот пример, как это можно реализовать:
- Измените
TaskDialogViewModel
, чтобы он принималUserSettings
через инициализатор:
@Observable final class TaskDialogViewModel: ObservableObject {
private var userSettings: UserSettings
@Published var taskDuration: Int = 0
init(userSettings: UserSettings) {
self.userSettings = userSettings
self.taskDuration = userSettings.defaultTaskDuration
}
func updateTaskDuration() {
taskDuration = userSettings.defaultTaskDuration
}
}
- Теперь создайте экземпляр
TaskDialogViewModel
в вашейView
и передайте в негоUserSettings
:
struct Dialog: View {
@EnvironmentObject var userSettings: UserSettings
@StateObject private var model: TaskDialogViewModel
init(userSettings: UserSettings) {
_model = StateObject(wrappedValue: TaskDialogViewModel(userSettings: userSettings))
}
var body: some View {
HStack {
// Ваш UI код
}
.onAppear {
model.updateTaskDuration()
}
}
}
- Убедитесь, что когда вы создаете
Dialog
, вы передаетеUserSettings
в качестве аргумента:
struct ContentView: View {
@EnvironmentObject var userSettings: UserSettings
var body: some View {
Dialog(userSettings: userSettings)
}
}
Таким образом, TaskDialogViewModel
теперь напрямую зависит от UserSettings
, который является @EnvironmentObject
, и вы устраните необходимость в передаче значений через View
. Когда значения в UserSettings
изменяются, вы можете вызывать updateTaskDuration
в вашем ViewModel, чтобы обновить состояние.
Обратите внимание, что, соединяя зависимости через инициализацию, вы создаете более чистую архитектуру, соответствующую принципам SwiftUI, и упрощаете поддержку и тестирование вашего кода.