How to modify state from state in SwiftUI
Issue #487
In case we have to modify state when another state is known, we can encapsulate all those states in ObservableObject and use onReceive to check the state we want to act on.
See code Avengers
If we were to modify state from within body function call, we will get warnings
Modifying state during view update, this will cause undefined behavior.
This is similar to the warning when we change state inside render in React
For example, when we get an image, we want to do some logic based on that image and modify result state. Here we use var objectWillChange = ObservableObjectPublisher() to notify state change, and because onReceive requires Publisher, we use let imagePublisher = PassthroughSubject<UIImage, Never>()
Note that we use $ prefix from a variable to form Binding
| 1 | import SwiftUI | 
Use Published
See ObservableObject
By default an ObservableObject synthesizes an objectWillChange publisher that emits the changed value before any of its @Published properties changes.
| 1 | class Contact: ObservableObject { | 
We should use @Published
| 1 | class ViewModel: ObservableObject { | 
Note that we should not use objectWillChange as
By default an ObservableObject synthesizes an objectWillChange publisher that emits the changed value before any of its @Published properties changes.
| 1 | .onReceive(viewModel.$image, perform: { image in | 
We need to manually notify using objectWillChange !! Maybe this is a SwiftUI bug
| 1 | private func detect(image: UIImage) { | 
If we remove the declaration of var objectWillChange = ObservableObjectPublisher(), then it works automatically
objectWillChange
Learn more about the history of objectWillChange
https://twitter.com/luka_bernardi/status/1155944329363349504?lang=no
In Beta 5 ObjectBinding is now defined in Combine as ObservableObject (the property wrapper is now @ObservedObject). There is also a new property wrapper @Published where we automatically synthesize the objectWillChange publisher and call it on willSet.
It’ll objectWillChange.send() in the property willSet it’s defined on.
It just removes the boilerplate that you had to write before but otherwise behaves the same.
State vs ObservedObject
If we were to use @State instead of @ObservedObject, it still compiles, but after we pick an image, which should change the image property of our viewModel, the view is not reloaded.
| 1 | struct MainView: View { | 
Note that we can’t use @Published inside struct
‘wrappedValue’ is unavailable: @Published is only available on properties of classes
@State is for internal usage within a view, and should use struct and primitive  data structure. SwiftUI keeps @State property in a separate memory place to preserve it during many reload cycles.
@Observabled is meant for sharing reference objects across views
To to use @State we should use struct, and to use onReceive we should introduce another Publisher like imagePublisher
| 1 | struct ViewModel { | 
The dollar sign for State to access nested properties, like $viewModel.image is called derived Binding, and is achieved via Keypath member lookup feature of Swift 5.1.
Take a look at projectedValue: Binding<Value> from State and subscript<Subject>(dynamicMember keyPath from Binding
| 1 | (iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) | 
| 1 | /// A value and a means to mutate it. | 
Read more
- https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/
- https://stackoverflow.com/questions/56551131/what-does-the-dollar-sign-do-in-this-example
- https://stackoverflow.com/questions/44037611/how-to-avoid-setstate-inside-render-when-state-depends-on-render?rq=1
- https://stackoverflow.com/questions/57459727/why-an-observedobject-array-is-not-updated-in-my-swiftui-application
- https://www.pointfree.co/blog/posts/30-swiftui-and-state-management-corrections
- https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject
- Safely Updating The View State