That's an interesting idea, to use an existential in the environment...
First off, the general problem that you run into is that the types any Book
is its own type (an existential, which is basically a "box" that can store a any value that conforms to the protocol Book
). This type itself, however, does not conform to Book
. And since the Book
protocol extends Observable
, it follows that any Book
does not conform to that either, which explains your first error message.
The second message basically comes from the same issue, any Book
is, unlike a type that conforms to Book
not a class type, i.e. it does not formally conform to AnyObject
.
To construct a @Bindable
for your book, you must first "unbox" it, and that is done by writing a generic function over the Book
protocol that then gets your book
existential passed as parameter. Inside that function the parameter is then the concrete "unboxed" instance of the type that conforms to Book
(and the protocols that inherits from).
struct TitleEditView: View {
@Environment(\.book) private var book
var body: some View {
textField(with: book)
}
private func textField(with unboxedBook: some Book) -> TextField<Text> {
@Bindable var bindableBook = unboxedBook
return TextField("Title", text: $bindableBook.title)
}
Unfortunately that seems a little ugly, I didn't immediately see a way to write this as a nice @ViewBuilder
function and omit the concrete return type. Neither did I see a pretty way to "return" the @Bindable
somehow.
Two other things I see:
When playing around I noticed that Book
is bound to @MainActor
. As I see it, that is a problem for the @Entry
in the EnvironmentValues
, as this macro stores the default value (a PreviewBook
in your case) in a static mutable property. This violates concurrency isolation, no idea why you did not get that error shown when compiling. 
Second:
While above unboxing (when not isolating Book
to @MainActor
) works, it's a little ugly and makes me wonder whether you really need this. The environment is usually set from a view higher up the hierarchy, why do you need to abstract values via a protocol? If Book
contains more than just "environment values", i.e. logic that needs to be different between preview and production code or the like, perhaps you want to reconsider encapsulation? Why can't a simple Book
struct (instead of that being a protocol) not just contain something like a isPreview
property (defaulting to false
?) and logic relying on that resides in a completely different type?
I am usually all for using protocols to properly define your various APIs, but here it looks like it's causing friction between the existing APIs SwiftUI offers, so maybe it's not the best use-case to rely on existentials in environment values like this. Perhaps somebody else can elaborate/advice on this?