How it works
FocusedValueKey is the protocol you adopt to declare a typed key for a value that travels with the currently focused view. Each conforming type names a Value associated type, and that key becomes the lookup token used to read and write an entry in FocusedValues. Reach for it when a command, toolbar, or detail view elsewhere in the hierarchy needs to react to whatever the user is currently editing — the focused view publishes a value under the key, and any observer keyed to the same type receives it, with nil standing in whenever nothing relevant is focused.
Declare a key by conforming to FocusedValueKey
A
FocusedValueKeyconformer is a lightweight type whose only job is to identify one slot of focus state by its own metatype. HereSelectedNoteKeyadopts the protocol and suppliestypealias Value = String, which fixes the type of data the key carries; the type itself is never instantiated, only used asSelectedNoteKey.self.Expose the value through a FocusedValues property
Keys are read and written by extending
FocusedValueswith a computed property whose getter and setter subscript into the bag with your key's metatype. TheselectedNoteproperty does exactly this —self[FocusedValueKeyDemo.SelectedNoteKey.self]— turning the raw key into a named, optional accessor (String?) that the rest of your code reads ergonomically.Publish a value from the focused view with focusedValue
Apply
.focusedValue(\.selectedNote, note)to the view that owns the focus to write into that key. While that view is focused, the bag reports the supplied value; here theTextField— marked focusable with.focused($isEditing)— pushes the currentnotetext up under theselectedNotekey.Read the value where focus matters
A consuming view declares
@FocusedValue(\.selectedNote)to observe the same key. It receives the published value when something focusable is publishing it andnilotherwise, so UI such as a menu item or status line can enable, disable, or update itself purely from what the user is editing.
typealias Value = String to typealias Value = Int and watch the compiler reject .focusedValue(\.selectedNote, note), since the key's Value type now no longer matches the String it is being handed.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 FocusedValueKeyDemo: View {
private struct SelectedNoteKey: FocusedValueKey {
typealias Value = String
}
@FocusState private var isEditing: Bool
@State private var note = "Meeting at 3pm"
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Focused Note")
.font(.headline)
TextField("Note", text: $note)
.textFieldStyle(.roundedBorder)
.focused($isEditing)
.focusedValue(\.selectedNote, note)
Text(isEditing ? "Editing: \(note)" : "Tap to focus")
.font(.footnote)
.foregroundStyle(.secondary)
}
.padding()
}
}
extension FocusedValues {
var selectedNote: String? {
get { self[FocusedValueKeyDemo.SelectedNoteKey.self] }
set { self[FocusedValueKeyDemo.SelectedNoteKey.self] = newValue }
}
}