Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FB16360042: SwiftUI @FocusedValue needs to provide a binding through projectedValue when given a Observable class #601

Open
sindresorhus opened this issue Jan 19, 2025 · 0 comments

Comments

@sindresorhus
Copy link
Member

  • Date: 2025-01-19
  • Resolution: Open
  • Area: SwiftUI
  • OS: macOS 15.2
  • Type: Incorrect/Unexpected Behavior

Description

When using a class with ObservableObject, we could use @FocusedObject and access its projectedValue through $ to get a binding. The replacement for @FocusedObject when using @Observable is @FocusedValue, but it does not have a projectedValue ($), so there is no way to get a binding to the class.

I previously had this with ObservableObject:

struct MenuToggle: View {
	@FocusedObject private var sceneData: SceneData?

	var body: some View {
		Toggle(
			"Unicorn",
			isOn: $sceneData?.isUnicorn ?? .constant(false)
		)
	}
}

I assumed I could move to this when using @Observable:

struct MenuToggle: View {
	@FocusedValue(SceneData.self) private var sceneData

	var body: some View {
		Toggle(
			"Unicorn",
			// No binding available...
			isOn: $sceneData?.isUnicorn ?? .constant(false)
		)
	}
}

But it does not provide a binding through $... It should!


As a workaround, I passed the binding into a FocusedScene value, but that doesn't seem like the proper solution.

struct MenuToggle: View {
	@FocusedValue(\.sceneData) private var sceneData

	var body: some View {
		Toggle(
			"Unicorn",
			isOn: sceneData?.isUnicorn ?? .constant(false)
		)
	}
}

extension FocusedValues {
	@Entry
	var sceneData: Binding<SceneData>?
}

For context, the ?? in the examples work with the binding because I have this:

extension Binding {
	static func ?? <T>(lhs: Binding<T?>, rhs: T) -> Binding<T> {
		.init(
			get: { lhs.wrappedValue ?? rhs },
			set: {
				lhs.wrappedValue = $0
			}
		)
	}

	static func ?? <T>(lhs: Binding<T?>, rhs: T?) -> Binding<T?> {
		.init(
			get: { lhs.wrappedValue ?? rhs },
			set: {
				lhs.wrappedValue = $0
			}
		)
	}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant