How it works
FocusedObject is a property wrapper that reads an observable object published into the focused view hierarchy, giving you a live handle on the model that belongs to whatever is currently in focus. Reach for it when a command, toolbar, status bar, or inspector needs to act on the object behind the active editor without that object being threaded down through bindings or the environment. Because the wrapped value tracks focus, it resolves to the right instance as focus moves and becomes nil when nothing focusable is supplying one. It is the ObservableObject counterpart to FocusedValue, observing the object so your view updates whenever its published properties change.
Publish the object with focusedSceneObject(_:)
An object only becomes available to FocusedObject once a view in the hierarchy exposes it for focus. Applying
.focusedSceneObject(document)to the editing view publishes thatDocumentinstance into the scene's focus context, where it can be picked up by any reader. Here theEditorview attaches it to itsTextField, so the document travels with the field's focus.Declare the reader as an optional ObservableObject
FocusedObject wraps an ObservableObject and always vends an optional, because there may be no focused object to read.
@FocusedObject private var document: Document?inStatusBarmatches the publishedDocumentby its type; SwiftUI fills it in with the focused instance, or leaves it nil when none is present.Read the wrapped value and observe its changes
Accessing the property yields the focused object directly, and because FocusedObject observes it, the view re-renders when the object's @Published members change.
StatusBarreadsdocument?.title, so as the boundtitleis edited through the field it stays in sync, falling back to "No document" via the nil-coalescing default when nothing is focused.Keep the type the matching key
FocusedObject identifies what to read purely by the declared object type, so the type used to publish and the type used to read must line up. The
Documentclass — afinal classconforming toObservableObject— is the shared key between.focusedSceneObject(document)on the producing side and@FocusedObject private var document: Document?on the consuming side.
$document.title, and watch the Label in StatusBar update its text through document?.title as focus stays on the field.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct FocusedObjectDemo: View {
final class Document: ObservableObject {
@Published var title: String = "Untitled.txt"
}
struct Editor: View {
@StateObject private var document = Document()
var body: some View {
TextField("Title", text: $document.title)
.textFieldStyle(.roundedBorder)
.focusedSceneObject(document)
}
}
struct StatusBar: View {
@FocusedObject private var document: Document?
var body: some View {
Label(document?.title ?? "No document", systemImage: "doc.text")
.font(.headline)
}
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Editor()
Divider()
StatusBar()
}
.padding()
}
}