What does the dollar sign do in this example?
Solution 1:
This is very well explained in WWDC 2019 video 415. You are merely looking at one special case of a broad language feature, namely property wrappers.
A property wrapper (such as @State
) is actually a way of referring to an instance of a type (usually a struct or enum) with the same name (such as State). The latter provides instructions for turning this instance property into a computed property whose getter and setter are the getter and setter for a certain computed property of itself (its wrappedValue
). It also typically holds private storage backing that computed property.
Thus, after the declaration
@State var showFavoritesOnly = true
...showFavoritesOnly
is turned into a computed property, with its getter and setter supplied by the State struct. When you set showFavoritesOnly
to true, that is routed through the State struct's setter and ends up in a stored property of the State instance.
All of this implies that somewhere there is a State instance associated with your showFavoritesOnly
. And there is, but it's hidden from view. Its name, in case you'd like to see that State instance, is _showFavoritesOnly
.
Okay, but when you say $showFavoritesOnly
, you do not get a State struct; you get a Binding struct. Why? That's because a property wrapper has a mechanism for specifying what the returned value from the $
name should be. In the case of State, it specifies that this value should be its own binding
property, which is a Binding (see the docs: https://developer.apple.com/documentation/swiftui/state).
By an amazing coincidence, Toggle's isOn
initializer takes a Binding (again, see the docs, https://developer.apple.com/documentation/swiftui/toggle/3232112-init). You could not have set the Toggle's isOn
to showFavoritesOnly
even if you wanted to! Instead, you set it to the Binding<Bool>
supplied by the State instance, so that the Toggle has automatic two-way communication with the State object. The SwiftUI framework enforces its own correct usage; a Toggle can exist only in relation to some binding that acts as the underlying source of truth for its on/off state. And because it's a binding, not a mere Bool, communication works in both directions: when the user taps the switch in the Toggle, the change in value flows "up" to the State variable by way of the binding.
Solution 2:
The $
is used in conjunction with property delegates.
It's not an operator, but a prefix (thanks @matt!).
For more about property delegates, see this Swift Evolution document.
e.g. in @State var aState = false
, State
is a property delegate.
This means that if we write:
-
aState
we're accessing aBool
value -
$aState
we're accessing aBinding<Bool>
value
Different property delegates will generate different values.