-
Notifications
You must be signed in to change notification settings - Fork 706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The reversed data flow from the standard SwiftUI views back to the AppState #59
Comments
Hey @mohammad19991 , thanks for the heads up! Not sure if I understood the case fully, here is the recap of the mechanics to make sure we're on the same page: There is an From what I see, the setter does assign the new value to the The second side effect of the setter is updating the So if I understand the problem correctly, you're referring to the rerender caused by setting the same values to the |
yes that's right @nalexn, I'm referring to the rerender caused by setting the same values to the @State. specifically am talking about this extension Binding {
typealias ValueClosure = (Value) -> Void
func onSet(_ perform: @escaping ValueClosure) -> Self {
return .init(get: { () -> Value in
self.wrappedValue
}, set: { value in
self.wrappedValue = value
perform(value)
})
}
} as I understand this function, every time we access it then it returns a new instance of And regarding filtering are you referring to this func updates<Value>(for keyPath: KeyPath<Output, Value>) ->
AnyPublisher<Value, Failure> where Value: Equatable {
return map(keyPath).removeDuplicates().eraseToAnyPublisher()
} |
Gotcha. That's an easy fix then: extension Binding where Value: Equatable {
typealias ValueClosure = (Value) -> Void
func onSet(_ perform: @escaping ValueClosure) -> Self {
return .init(get: { () -> Value in
self.wrappedValue
}, set: { value in
if self.wrappedValue != value {
self.wrappedValue = value
}
perform(value)
})
}
} I've pushed the fix. Thanks for reporting! |
Thanks, I'll test it and let you know. |
@nalexn it's not working, to help you understand more about the issue I made this project to demonstrate the issue, there are 2 branches the first one is the
I hope this makes it more clear for you to understand the problem, please let me know if you need more details. |
Another concern I would mention that having two |
Hey, yep, I'll have a look, thanks for composing a sample! |
Hey @mohammad19991 , I've done some experiments based on the points you've made. To fully understand what's going on I was adding if #available(iOS 15.0, *) {
print("\(Self._printChanges())")
} inside So if we take the original version, which gets "re-rendered" when we press the button, the following sequence takes place:
Now, if we keep all the
Although in both cases we have only the I've tried to address the issue with duplicated routing state ( There were two approaches I've taken - first was to feed the views with
extension Store {
func binding<Value>(for keyPath: WritableKeyPath<Output, Value>) -> Binding<Value> where Value: Equatable {
return .init(get: {
self.value[keyPath: keyPath]
}, set: { newValue in
self.value[keyPath: keyPath] = newValue
})
}
} so that the struct ContentView: View {
@Binding var routing: Routing
init(_ routing: Binding<Routing>) {
_routing = routing
}
....
}
@main
struct CleanArchRoutingApp: App {
@Environment(\.injected) var injected
var body: some Scene {
WindowGroup {
NavigationView {
ContentView(injected.appState.binding(for: \.routing.contentView))
}
}
}
} In this case, the My understanding is that SwiftUI hierarchy requires explicit notification from In this example, the root view has reference to the mutated object though The updates start to come as you change
The result was the same as a couple of years ago when I first noticed the downside of Conclusion: I still cannot see a more optimal way to organize programmatic navigation in SwiftUI than As SwiftUI remains a black box, I cannot tell why in seemingly identical cases it either reconstructs the |
Hi @nalexn
I like what you did with this clean-architecture approach, I read some of your articles related to SwiftUI State and I saw the below implementation of reversing the data flow from views to AppState:
where you will use it like this:
It works in terms of updating the
AppState
, but if we look at the implementation ofdispatched/onSet
function we notice that it wraps the Value in a new Binding instance and this will cause the view to rerender again while it's not needed, I wanna check if you are aware about this case or maybe I didn't understand the idea properly.I can show you and example if needed.
The text was updated successfully, but these errors were encountered: