UIViewRepresentable Coordinator @Binding returns stale value when accessed via context.coordinator but fresh value when accessed via self

I'm encountering unexpected behavior with @Binding in a UIViewRepresentable's Coordinator. The same Coordinator instance returns different values depending on how the property is accessed.

Environment:

  • iOS 17+ / Xcode 15+
  • SwiftUI with UIViewRepresentable

Issue: When I access @Binding var test inside the Coordinator:

  • ✅ Via self.test in Coordinator methods: Returns the updated value
  • ❌ Via context.coordinator.test in updateUIView: Returns the stale/initial value

Both access the same Coordinator instance (verified by memory address), yet return different values.

Minimal Reproducible Example:

struct ContentView: View {
    @State private var test: [Int] = [1, 2, 3, 4, 5]
    
    var body: some View {
        VStack {
            TestRepresentable(test: $test)
            Text("State: \(test.description)")
        }
    }
}

struct TestRepresentable: UIViewRepresentable {
    @Binding var test: [Int]
    
    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle("Toggle", for: .normal)
        button.addTarget(
            context.coordinator,
            action: #selector(Coordinator.buttonTapped),
            for: .touchUpInside
        )
        return button
    }
    
    func updateUIView(_ uiView: UIButton, context: Context) {
        // Log coordinator instance address
        let coordAddr = String(describing: Unmanaged.passUnretained(context.coordinator).toOpaque())
        print("[updateUIView] Coordinator address: \(coordAddr)")
        
        // Log values
        print("[updateUIView] self.test: \(self.test)")
        print("[updateUIView] context.coordinator.test: \(context.coordinator.test)")
        
        // These should be the same but they're not!
        // self.test shows updated value
        // context.coordinator.test shows stale value
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(test: $test)
    }
    
    class Coordinator: NSObject {
        @Binding var test: [Int]
        var idx: Int = 0
        
        init(test: Binding<[Int]>) {
            _test = test
        }
        
        @objc func buttonTapped() {
            idx += 1
            if idx < test.count {
                test[idx] += 5
            }
            
            // Log coordinator instance address
            let selfAddr = String(describing: Unmanaged.passUnretained(self).toOpaque())
            print("[buttonTapped] Coordinator address: \(selfAddr)")
            
            // Log value - this shows the UPDATED value
            print("[buttonTapped] self.test: \(test)")
        }
    }
}

Actual Output:

[Initial]
[updateUIView] Coordinator address: 0x600001234567
[updateUIView] self.test: [1, 2, 3, 4, 5]
[updateUIView] context.coordinator.test: [1, 2, 3, 4, 5]

[After first tap]
[buttonTapped] Coordinator address: 0x600001234567
[buttonTapped] self.test: [1, 7, 3, 4, 5]  ✅ Updated!

[updateUIView] Coordinator address: 0x600001234567  ← Same instance
[updateUIView] self.test: [1, 7, 3, 4, 5]  ✅ Updated!
[updateUIView] context.coordinator.test: [1, 2, 3, 4, 5]  ❌ Stale!

[After second tap]
[buttonTapped] Coordinator address: 0x600001234567
[buttonTapped] self.test: [1, 7, 8, 4, 5]  ✅ Updated!

[updateUIView] Coordinator address: 0x600001234567  ← Same instance
[updateUIView] self.test: [1, 7, 8, 4, 5]  ✅ Updated!
[updateUIView] context.coordinator.test: [1, 2, 3, 4, 5]  ❌ Still stale!

Questions:

  1. Why does context.coordinator.test return a stale value when it's the same Coordinator instance?
  2. Is this intended behavior or a bug?
  3. What's the correct pattern to access Coordinator's @Binding properties in updateUIView?

Workaround Found:

Using self.test instead of context.coordinator.test in updateUIView works, but I'd like to understand why accessing the same property through context yields different results.

Related:

  • I've seen suggestions to update coordinator.parent = self in updateUIView, but this doesn't explain why the same object's property returns different values.
  • Binding documentation states it's "a pair of get and set closures," but there's no explanation of how Context affects closure behavior.

Any insights would be greatly appreciated!

P.S - https://stackoverflow.com/questions/69552418/why-does-a-binding-in-uiviewrepresentables-coordinator-have-a-constant-read-valu

This is the link that I found out online with same problem that I have but not answered well.

UIViewRepresentable Coordinator &#64;Binding returns stale value when accessed via context.coordinator but fresh value when accessed via self
 
 
Q