Using The .focusedValue Modifier and @FocusedBinding Property Wrapper In SwiftUI

Daily Coding Tip 041

This is a new way to pass data between Views. Instead of having an ObservableObject, we save data using a FocusedValueKey.

In the following example, DisplayTextView is able to show the text you type into TextFieldView, despite the fact that a Binding<String> or String constant is not passed between the Views.

The magic here is enabled by the structure and extension at the bottom.

The FocusedValueKey protocol requires that conforming structures have a typealias for the value they store.

Once you have a structure that defines a typealias for your key, you’ll need to define a getter and setter for that value. This extension of FocusedValue defines \.text as the key that we will use to read and write the value. Notice that the getter and setter both use the FocusedTextKey type as a subscript for FocusedValues. Now we just need to write a value to the key in TextFieldView, and then we need to read from it in DisplayTextView.

The .focusedValue(\.text, $text) modifier on the TextField saves the value to the key.

The @FocusedBinding(\.text) var text: String? property in DisplayTextView subscribes it to changes in the associated value. Notice that it is an optional, because the value does not have to be set. As Apple’s official documentation says, “Unlike EnvironmentKey, FocusedValuesHostKey has no default value requirement, because the default value for a key is always nil.” I assume the original name was FocusedValuesHostKey, because the documentation still mentions this despite the fact it no longer exists.

In other words, you can set up a key without giving it a value, and your code will still run.

When you do set up a value, you will need to unwrap it as I did using the nil coalescing operator ‘??’.

The next question you might have is why these values would be needed. After all, they seem to be global, at least in the context of a single window. We wouldn’t want to keep all our data at this scope, and having a lot of them might make it hard to debug. A more complex example by an Apple Frameworks Engineer on the Apple Developer Forums shows an interesting use case. When a Mac app has separate commands, such as Shift, Cmd + D in that example, you may still want to access data in the app despite the fact that the commands are at the WindowGroup scope.

Now you can!


Get more Daily Coding Tips in your inbox!