PhaseAnimator doesn't reflect @Observable state changes after animation settles

I ran into a behavior with PhaseAnimator that I'm not sure is a bug or by design. I'd appreciate any insight.

The Problem

When an @Observable property is read only inside a PhaseAnimator content closure, changes to that property are ignored after the animation cycle completes and reaches its resting state. The UI gets stuck showing stale data.

Minimal Reproduction

I've put together a simple demo with two views side by side, both driven by the same ViewModel and toggled by the same button:

  • BrokenView — receives an @Observable object and reads its property inside PhaseAnimator. After the animation completes, toggling the property has no visible effect.
  • FixedView — receives the same value as a Bool parameter. Updates correctly every time because view's parameter has changed.
import SwiftUI

@Observable class ViewModel {
    var isError = false
}

struct BrokenView: View {
    let viewModel: ViewModel
    @State private var trigger = false

    var body: some View {
        VStack(spacing: 20) {
            Text("Broken (@Observable)").font(.headline)
            PhaseAnimator([false, true], trigger: trigger) { _ in
                if viewModel.isError {
                    Text("Error!").foregroundStyle(.red).font(.largeTitle)
                } else {
                    Text("OK").foregroundStyle(.green).font(.largeTitle)
                }
            }
        }
        .padding()
        .onAppear { trigger = true }
    }
}

struct FixedView: View {
    let isError: Bool
    @State private var trigger = false

    var body: some View {
        VStack(spacing: 20) {
            Text("Fixed (Value Type)").font(.headline)
            PhaseAnimator([false, true], trigger: trigger) { _ in
                if isError {
                    Text("Error!").foregroundStyle(.red).font(.largeTitle)
                } else {
                    Text("OK").foregroundStyle(.green).font(.largeTitle)
                }
            }
        }
        .padding()
        .onAppear { trigger = true }
    }
}

struct DemoView: View {
    @State private var viewModel = ViewModel()

    var body: some View {
        VStack(spacing: 40) {
            BrokenView(viewModel: viewModel)
            Divider()
            FixedView(isError: viewModel.isError)
            Divider()
            Button("Toggle isError: \(viewModel.isError)") {
                viewModel.isError.toggle()
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

Run the preview, then tap the toggle button. FixedView updates instantly; BrokenView stays stuck.

My Understanding

It seems like PhaseAnimator only tracks @Observable access during active animation phases. Once it settles at rest, the content closure is not re-evaluated, so observation tracking is effectively lost. Passing a value type works because SwiftUI view diffing detects the input change and triggers a body re-evaluation, which in turn re-evaluates the PhaseAnimator content.

Question

Is this intended behavior? Or shouldn't I use phase animator in this way? I could not find any mention of this limitation in the documentation. If it is by design, it might be worth documenting — it is a subtle pitfall that is easy to miss.

Thanks in advance for any input!

PhaseAnimator doesn't reflect @Observable state changes after animation settles
 
 
Q