How it works
FocusedValue is a property wrapper that reads a value published by whichever view currently holds focus. Where a regular binding ties one view to one piece of state, FocusedValue lets a distant part of the interface — a toolbar, an inspector, a menu command — observe data exposed by the focused view without that view passing a binding down through every intervening container. Reach for it when an action's meaning depends on what the user is working in right now, such as a Rename command that should operate on the note that currently has focus. The wrapper produces an optional, which becomes nil whenever nothing focused is publishing the requested value.
Define a key by conforming to FocusedValueKey
Each focused value is identified by a type that conforms to
FocusedValueKeyand declares the kind of data it carries through itsValueassociated type. In the example,SelectedNoteKeysetstypealias Value = String, so this key always identifies aStringflowing from the focused view to its observers.Read the value with @FocusedValue
Apply the
@FocusedValueproperty wrapper, passing the key type to its initializer, to subscribe to whatever the focused view currently publishes for that key. Here@FocusedValue(SelectedNoteKey.self) private var focusedNotemakesfocusedNotetrack the live value; because no focused view may be supplying it, its wrapped type is an optionalString?.Publish the value with focusedValue(_:_:)
On the producing side, the
focusedValue(_:_:)view modifier attaches a value to a view under a given key, so that the value is exposed only while that view is focused. TheTextFieldcalls.focusedValue(SelectedNoteKey.self, note), advertising the currentnotestring for the same key the reader watches.Drive it with the focus system
FocusedValuefollows SwiftUI's focus, so the published value is visible to readers only while the producing view actually holds focus. TheTextFieldparticipates via.focused($isEditing), and when focus leaves it the key stops publishing andfocusedNotereverts tonil.Handle the optional result
Because the wrapped value is optional, always account for the no-focus case when you use it. The example unwraps with a default —
Text("Focused note: \(focusedNote ?? "none")")— so the label reads the live note while editing and showsnoneonce focus moves away.
TextField bound to its own @State string and give it .focusedValue(SelectedNoteKey.self, otherNote) too, then watch focusedNote switch between the two notes as you move focus between the fields.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 FocusedValueDemo: View {
private struct SelectedNoteKey: FocusedValueKey {
typealias Value = String
}
@State private var note = "Draft"
@FocusState private var isEditing: Bool
@FocusedValue(SelectedNoteKey.self) private var focusedNote
var body: some View {
VStack(alignment: .leading, spacing: 12) {
TextField("Note", text: $note)
.textFieldStyle(.roundedBorder)
.focused($isEditing)
.focusedValue(SelectedNoteKey.self, note)
Text("Focused note: \(focusedNote ?? "none")")
.font(.footnote)
.foregroundStyle(.secondary)
}
.padding()
}
}