Principles of Data Flow
Every time we read a piece of data in our view, we’re creating a dependency for that view.
Every time the data changes, the view has to change (read: re-render) to reflect the new value.
Every piece of data that we’re reading in the view hierarchy has a source of truth.
The source of truth can live in the view hierarchy (if view-related, like a
collapsedstatus), or externally (our models).We should always have a single source of truth (no duplicate values).
@State
When a property has an associated property wrapper
@State, SwiftUI knows that it needs to persist the storage across multiple updates of the same view.Mark
@Stateproperty as private, to really enforce the idea that state is owned and managed by that view specifically.@Stateproperties are a special case of state variables, as SwiftUI knows when they change.And because SwiftUI knows that the
@Statevariables change the viewbodylook, SwiftUI knows that the view rendering depend on their state.When a
@Stateproperty changes, in the runtime SwiftUI will recompute the body of that view and all of its children.All the changes always flow down through your view hierarchy.
We’re able to do these re-rendering very efficiently because SwiftUI compares the views and renders again only what is changed.
Views are a function of state, not of a sequence of events.
@Binding
By using the
Bindingproperty wrapper, we define an explicit dependency to a source of truth without owning it.We don’t need to provide an initial value because the binding can be derived from a state.
Primitives views such as
Toggle,TextField, andSliderall expect a binding. We can read and write a@bindingproperty (without owning it!)
Publishers (External changes)
In case the event comes from an external source (a.k.a. not within the views), we use
Combine’sPublishersIt’s very important for
Views to receive events on the main thread.
ObservableObject (External Data)
When the data is outside our View, swiftUI needs to know how to react on changes of that data
By making our external data conform to
ObservableObjectprotocol, we are required to have a publisher, accessible via the synthesizedobjectWillChangeproperty, that fires every time the data is about to change.All we need to do later is to associate an
@ObservedObjectproperty wrapper in the model and pass theObservedObjectto the view in its initialization.While SwiftUI
Views are value types (Structs), any time we’re using a reference type, we should be using the@ObservedObjectproperty wrapper: this way SwiftUI will know when that data changes and can keep our view hierarchy up to date.
Environment
Thanks to the
.environmentmodifier, we can add ourObservableObjects into the environment.By using this
@EnvironemntObjectproperty wrapper, we can create a dependency to these environment objects.
Object Binding vs Environment
We can build your whole app with
@Binding, but it can get kind of cumbersome to pass around the model from view to view.EnvironmentObjectis a convenience for passing data around your hierarchy indirectly (without the need to pass the object to each view first).
@State vs @ObservedObject
@Stateis great for data that’s view local, a value type, managed.@Stateis allocated, created and managed by SwiftUI@Stateis value type@ObservedObjectis for representing external data to SwiftUI, such as a database.@ObservedObjectis storage that we manage, which makes it great for the model that we already have.@ObservedObjectis managed by us@ObservedObjectis reference type
