Передать свойство от ObservableObject в другой класс ObObject?

Вопрос или проблема

Предположим, у меня есть 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. Вот пример, как это можно реализовать:

  1. Измените 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
    }
}
  1. Теперь создайте экземпляр 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()
        }
    }
}
  1. Убедитесь, что когда вы создаете Dialog, вы передаете UserSettings в качестве аргумента:
struct ContentView: View {
    @EnvironmentObject var userSettings: UserSettings

    var body: some View {
        Dialog(userSettings: userSettings)
    }
}

Таким образом, TaskDialogViewModel теперь напрямую зависит от UserSettings, который является @EnvironmentObject, и вы устраните необходимость в передаче значений через View. Когда значения в UserSettings изменяются, вы можете вызывать updateTaskDuration в вашем ViewModel, чтобы обновить состояние.

Обратите внимание, что, соединяя зависимости через инициализацию, вы создаете более чистую архитектуру, соответствующую принципам SwiftUI, и упрощаете поддержку и тестирование вашего кода.

Оцените материал
Добавить комментарий

Капча загружается...