import ComposableArchitecture import UIKit @Reducer struct Counter { @ObservableState struct State: Equatable, Identifiable { let id = UUID() var count = 0 } enum Action { case decrementButtonTapped case incrementButtonTapped } var body: some Reducer { Reduce { state, action in switch action { case .decrementButtonTapped: state.count -= 1 return .none case .incrementButtonTapped: state.count += 1 return .none } } } } final class CounterViewController: UIViewController { private let store: StoreOf init(store: StoreOf) { self.store = store super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground let decrementButton = UIButton(type: .system) decrementButton.addTarget(self, action: #selector(decrementButtonTapped), for: .touchUpInside) decrementButton.setTitle("−", for: .normal) let countLabel = UILabel() countLabel.font = .monospacedDigitSystemFont(ofSize: 17, weight: .regular) let incrementButton = UIButton(type: .system) incrementButton.addTarget(self, action: #selector(incrementButtonTapped), for: .touchUpInside) incrementButton.setTitle("+", for: .normal) let rootStackView = UIStackView(arrangedSubviews: [ decrementButton, countLabel, incrementButton, ]) rootStackView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(rootStackView) NSLayoutConstraint.activate([ rootStackView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor), rootStackView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor), ]) observe { [weak self] in guard let self else { return } countLabel.text = "\(store.count)" } } @objc private func decrementButtonTapped() { store.send(.decrementButtonTapped) } @objc private func incrementButtonTapped() { store.send(.incrementButtonTapped) } } #Preview { CounterViewController( store: Store(initialState: Counter.State()) { Counter() } ) }