我正在开发一个SwiftUI项目,其中有一个集中化的应用程序状态架构(类似于Redux)。此应用程序状态类的类型为ObservableObject,并直接使用@EnvironmentObject绑定到SwiftUI视图类。
以上内容适用于小型应用程序。但是随着视图层次结构变得越来越复杂,开始出现性能问题。原因是,即使视图可能只需要一个属性,ObservableObject也会向已订阅的每个视图触发更新。
我解决这个问题的想法是在全局应用程序状态和视图之间放置一个模型视图。模型视图应具有全局应用程序状态的属性子集,这些属性由特定视图使用。它应该订阅全局应用程序状态并接收每次更改的通知。但是模型视图本身仅应触发视图更新以更改全局应用程序状态的子集。
因此,我将不得不在两个可观察对象(全局应用程序状态和模型视图)之间架起桥梁。该怎么办?
这是草图:
class AppState: ObservableObject {
@Published var propertyA: Int
@Published var propertyB: Int
}
class ModelView: ObservableObject {
@Published var propertyA: Int
}
struct DemoView: View {
@ObservedObject private var modelView: ModelView
var body: some View {
Text("Property A has value \($modelView.propertyA)")
}
}
答案 0 :(得分:0)
这是可行的方法
class ModelView: ObservableObject {
@Published var propertyA: Int = 0
private var subscribers = Set<AnyCancellable>()
init(global: AppState) {
global.$propertyA
.receive(on: RunLoop.main)
.assign(to: \.propertyA, on: self)
.store(in: &subscribers)
}
}
答案 1 :(得分:0)
您提到的“桥梁”通常称为派生状态。
这是一种实现redux Connect
组件的方法。当派生状态更改时,它会重新渲染...
public typealias StateSelector<State, SubState> = (State) -> SubState
public struct Connect<State, SubState: Equatable, Content: View>: View {
// MARK: Public
public var store: Store<State>
public var selector: StateSelector<State, SubState>
public var content: (SubState) -> Content
public init(
store: Store<State>,
selector: @escaping StateSelector<State, SubState>,
content: @escaping (SubState) -> Content)
{
self.store = store
self.selector = selector
self.content = content
}
public var body: some View {
Group {
(state ?? selector(store.state)).map(content)
}.onReceive(store.uniqueSubStatePublisher(selector)) { state in
self.state = state
}
}
// MARK: Private
@SwiftUI.State private var state: SubState?
}
要使用它,请传入商店和“选择器”,它们会将应用程序状态转换为派生状态:
struct MyView: View {
var index: Int
// Define your derived state
struct MyDerivedState: Equatable {
var age: Int
var name: String
}
// Inject your store
@EnvironmentObject var store: AppStore
// Connect to the store
var body: some View {
Connect(store: store, selector: selector, content: body)
}
// Render something using the selected state
private func body(_ state: MyDerivedState) -> some View {
Text("Hello \(state.name)!")
}
// Setup a state selector
private func selector(_ state: AppState) -> MyDerivedState {
.init(age: state.age, name: state.names[index])
}
}
您可以看到完整的实现here