iosswiftswift-composable-architecturetca

TCA: 'onAppear' Action is triggered but changes are not shown on screen


I'm using the TCA for an SwiftUI application and I have this bug that the '.onAppear' action is not showing any changes on the screen. This happens in the simulator and also on the phone. The weird thing is that when I'm printing the changes of the State with ._printChanges() everything is shown correctly.

I'm running Xcode 14.3 and using the prerelease/1.0 branch, the same happened on the navigation-beta branch.

What am I missing?

Here's some code to reproduce:


import SwiftUI

import ComposableArchitecture

struct PlayAround: ReducerProtocol {
  struct State: Equatable {
    var text: String = ""
  }

  enum Action: Equatable {
    case setText(String)
    case onAppear
  }

  var body: some ReducerProtocol<State, Action> {
    Reduce { state, action in
      switch action {
      case .setText(let newText):
        state.text = newText
        return .none
      case .onAppear:
        state.text = "On Appear"
        return .none
      }
    }
    ._printChanges()
  }
}

struct PlayAroundView: View {
  let store: StoreOf<PlayAround>

  init(store: StoreOf<PlayAround>) {
    self.store = store
  }

  var body: some View {
    WithViewStore(store, observe: { $0 }) { viewStore in
      VStack {

        Text(viewStore.text)

        Button {
          viewStore.send(.setText("Send Button"))
        } label: {
          Text("Send text")
        }
      }
      .onAppear {
        viewStore.send(.onAppear)
      }
    }
  }
}

struct PlayAround_Previews: PreviewProvider {
  static var previews: some View {
    NavigationStack {
      PlayAroundView(
        store: .init(
          initialState: PlayAround.State(text: "Init"),
          reducer: PlayAround()
        )
      )
    }
  }
}

I'm expecting to see the text to be "On Appear" but it stays on the text giving in the initialiser.


Solution

  • So this seems to be a bug that happens on the first view of the application only. If you create a RootView which only purpose is to show the actual first view everything works as expected.