Using Observation in a Protocol throws compiler error

I have boiled down a problem from a large project to the following fragment that generates a compiler error as shown.

It seems to me that in XYGrid, v is of type T which is known to conform to P and hence be Observable. However, when I try an create a binding to it I run into problems/ Any insight would be appreciated.

@Observable
protocol P { }


struct XYGrid<T: P> : View {
    var v: T
    var body: some View {
        @Bindable var bv = v
            //'init(wrappedValue:)' is unavailable: The wrapped value must be an object that conforms to Observable
        Text("Hmmm...")
    }
}

There were compilation errors applying macro to a protocol - visible when expanded. Examining the errors revealed that @Observable as currently written will not work on protocols (it has implementation). Armed with this knowledge I worked around the problem. If there is any interest I can post.

1 Like

Please post. There's nothing worse than looking for an error message on Internet, and find a random page where some person describes the same issue, but concludes with "I fixed it, bye :stuck_out_tongue_winking_eye:" without sharing any solution, or even a hint. Nothing forces you to share, of course. But by default if you have a few minutes, share. Maybe some people will be interested in the near of far future, long after you've gone and you've even forgotten about the issue.

4 Likes

6 Likes

I hope some Xcode engineers are lurking, because this usability issue can be pretty frustrating! Any chance future versions can surface errors inside unexpanded macros to the attribute itself?

He can correct me if I'm wrong, but I believe the following would be an alternative implementation that compiles:

protocol P: Observable {}
2 Likes

By popular request, here is a little analysis...

@Observable inserts some code into the protocol. When the macro is expanded, no errors show. However, when I paste the macro implementation into another protocol P1 I get errors.

The penny drops... from these errors, it is obvious that @Observable will not work on a protocol - the code it generates will only work on a class. It seems then that the compiler reports no errors adding @Observable to a Protocol.

The fix then is not to try and add Observation to the protocol but to the classes that adopt the protocol directly. It's a little clunky but clear. It would be nice to enforce it in the protocol but there you go.

So, the following code compiles... and also seems to both compile and work in my main project where all this originated.

protocol P: AnyObject { var i: Int { get set }}

@Observable final class C: P { var i = 1 }

struct S<T: P & Observation.Observable> : View {
    var t: T
    var body: some View {
        @Bindable var t = t
        Text( "\(t.i)" )
        Picker( "", selection: $t.i ){}
    }
}

struct S1: View {
    var body: some View {
        var c = C()
        S<C>(t: c)
    }
}

I may not have got the very bottom of this but so far this is what I have found.

3 Likes