在SwiftUI中,环境@StateObject应该是单例的吗?



警告,这是环境变量的特殊情况。我并不是说所有的@StateObject都应该是单例的(像ViewModels)!在我看来,这是错误的。

在SwiftUI iOS应用程序中,我使用ObservableObject类进行设置,@Published属性:
final class Settings: ObservableObject {
   @Published var disableLockScreen = false // Just a dummy example
}

@main应用程序结构中,我用@StateObject初始化它并将其注入我的视图层次结构中:

@main
struct MyTestApp: App {
   @StateObject private var settings = Settings()
   var body: some Scene {
      MainView()
         .environmentObject(settings)
   }
}

所以MyTestApp拥有Settings的实例,所有子视图都可以通过@EnvironmentObject var settings: Settings访问它。
OK…这对视图来说非常方便。但是…

我不需要只在视图中访问这个Settings类。我还需要它在ViewModels不是View结构,但ObservableObject类。在ViewModel中,我可能想在代码部分访问Settings的属性(不直接链接到视图)。例如,我有UIApplication.shared.isIdleTimerDisabled = settings.disableLockScreen .

让我的ViewModels访问Settings,我必须在它们的init中注入它。就像这里和这里解释的那样。当然,这是可能的,但说实话,它生成的代码不多,而且阅读起来要复杂得多。

所以我找到了这个公认的答案,它显示了一个存储在@StateObject中的Singleton示例。我当时正在考虑改变我的Settings类是一个单例。在MyTestApp中,我只使用Settings.shared更改Settings()以在应用程序中存储Singleton实例,并继续将其提供给子视图:

@main
struct MyTestApp: App {
   @StateObject private var settings = Settings.shared
   var body: some Scene {
      MainView()
         .environmentObject(settings)
   }
}

真正变得更好的地方,是在ViewModels:他们现在可以简单地访问Settings.shared实例,如果他们需要访问它的属性。不需要大量代码的依赖注入。

在我看来,它真的很简单,流畅和正确。但我一点也不确定。使用Singleton作为在环境中共享的@StateObject是一个好方法吗?有什么我没看到的可能会引起问题的地方吗?是否所有@StateObject共享到环境中并由View以外的人使用的都应该是singleton ?

当您需要将视图状态作为引用类型对象时,请将@StateObject视为@State,现在我们有.task,这应该不经常。此外,您通常不会在App结构体中使用@StateObject,因为当没有使用其属性时,每次更改都会不必要地重新计算主体。State(在这两种情况下)声明了一个真实的状态,你永远不会传递可能改变的参数到它们的初始化,因为它们没有办法监视变化。

对于您的Settings,如果它只是一些简单的变量,那么它可以是一个结构体,您可以使用自定义的EnvironmentKey使其无处不在。如果您需要访问自定义结构中的设置(声明为@State var customStruct = CustomStruct),则使其符合DynamicProperty并在func update()中访问它。如果@Environment发生变化,就会调用update,与View调用body的方式相同。您可以将任何其他相关@State@StateObject移动到DynamicProperty中。例如

struct CustomStruct: DynamicProperty {
    @Environment(.settings) var settings
    @State var ...
    @StateObject var ...
    func update() {
        // environment or state values changed
    }
}

如果设置是保存模型数据,你需要一个引用类型,例如,你正在做一些异步加载或保存,那么你可以使用一个单例并传递它作为environmentObject,例如

@main
struct MyTestApp: App {
   var body: some Scene {
      MainView()
         .environmentObject(Settings.shared)
   }
}

和预览

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
.environmentObject(Settings.preview)
    }
}

相关内容

  • 没有找到相关文章

最新更新