Ответ
Размещение синглтона внутри ObservedObject создает архитектурные проблемы и может нарушить реактивность SwiftUI.
Основные проблемы:
-
Нарушение реактивности – SwiftUI отслеживает изменения только самого
@PublishedсвойстваObservedObject. Если синглтон изменяется внутри, но свойство-обертка не меняется, View не обновится. -
Скрытая зависимость – Наличие синглтона внутри ViewModel скрывает реальную зависимость, усложняя понимание кода.
-
Нарушение принципа единой ответственности – ViewModel становится посредником без добавленной ценности.
Пример проблемного кода:
class AppSettings {
static let shared = AppSettings()
var themeColor: Color = .blue
}
class ContentViewModel: ObservableObject {
// Проблема: изменения в shared.themeColor не вызовут обновление View
let settings = AppSettings.shared
}
struct ContentView: View {
@ObservedObject var viewModel = ContentViewModel()
var body: some View {
Text("Hello")
.foregroundColor(viewModel.settings.themeColor) // Не обновится при изменении themeColor
}
}
Правильные подходы:
1. Прямое использование как ObservableObject:
// Синглтон должен быть ObservableObject
class AppSettings: ObservableObject {
static let shared = AppSettings()
@Published var themeColor: Color = .blue
}
struct ContentView: View {
@ObservedObject var settings = AppSettings.shared
var body: some View {
Text("Hello")
.foregroundColor(settings.themeColor) // Теперь обновится корректно
}
}
2. Использование EnvironmentObject:
// В корне приложения
ContentView()
.environmentObject(AppSettings.shared)
// В дочерней View
struct ChildView: View {
@EnvironmentObject var settings: AppSettings
var body: some View {
Text("Hello")
.foregroundColor(settings.themeColor)
}
}
3. Внедрение зависимости через протокол:
protocol SettingsProvider {
var themeColor: Color { get set }
}
class ContentViewModel: ObservableObject {
@Published var settings: SettingsProvider
init(settings: SettingsProvider = AppSettings.shared) {
self.settings = settings
}
}
Вывод: Избегайте помещения синглтона в ObservedObject как простого свойства. Вместо этого используйте синглтон напрямую как ObservableObject или через EnvironmentObject.