init(_:observe:removeDuplicates:content:file:line:)

Initializes a structure that transforms a Store into an observable ViewStore in order to compute views from state.

WithViewStore.swift:583
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.