init(_:observe:removeDuplicates:content:file:line:)
Initializes a structure that transforms a Store
into an observable ViewStore
in order to compute views from state.
- iOS
- deprecated
- macOS
- deprecated
- tvOS
- deprecated
- watchOS
- deprecated
@MainActor init<State>(_ store: Store<State, ViewAction>, observe toViewState: @escaping (_ state: State) -> ViewState, removeDuplicates isDuplicate: @escaping (_ lhs: ViewState, _ rhs: ViewState) -> Bool, @ViewBuilder content: @escaping (_ viewStore: ViewStore<ViewState, ViewAction>) -> Content, file: StaticString = #fileID, line: UInt = #line)
Parameters
- store
A store.
- toViewState
A function that transforms store state into observable view state. All changes to the view state will cause the
WithViewStore
to re-compute its view.- isDuplicate
A function to determine when two
ViewState
values are equal. When values are equal, repeat view computations are removed.- content
A function that can generate content from a view store.
- file
The file.
- line
The line.
WithViewStore
will re-compute its body for any change to the state it holds. Often the Store
that we want to observe holds onto a lot more state than is necessary to render a view. It may hold onto the state of child features, or internal state for its logic.
It can be important to transform the Store
’s state into something smaller for observation. This will help minimize the number of times your view re-computes its body, and can even avoid certain SwiftUI bugs that happen due to over-rendering.
The way to do this is to use the observe
argument of this initializer. It allows you to turn the full state into a smaller data type, and only changes to that data type will trigger a body re-computation.
For example, if your application uses a tab view, then the root state may hold the state for each tab as well as the currently selected tab:
@Reducer
struct AppFeature {
enum Tab { case activity, search, profile }
struct State {
var activity: Activity.State
var search: Search.State
var profile: Profile.State
var selectedTab: Tab
}
// ...
}
In order to construct a tab view you need to observe this state because changes to selectedTab
need to make SwiftUI update the visual state of the UI. However, you do not need to observe changes to activity
, search
and profile
. Those are only necessary for those child features, and changes to that state should not cause our tab view to re-compute itself.
struct AppView: View {
let store: StoreOf<AppFeature>
var body: some View {
WithViewStore(self.store, observe: \.selectedTab) { viewStore in
TabView(selection: viewStore.binding(send: { .tabSelected($0) }) {
ActivityView(
store: self.store.scope(state: \.activity, action: \.activity)
)
.tag(AppFeature.Tab.activity)
SearchView(
store: self.store.scope(state: \.search, action: \.search)
)
.tag(AppFeature.Tab.search)
ProfileView(
store: self.store.scope(state: \.profile, action: \.profile)
)
.tag(AppFeature.Tab.profile)
}
}
}
}
To read more about this performance technique, read the Performance article.