Whereas investigating efficiency points in an app I am engaged on, I observed {that a} view that incorporates a customized binding will all the time be rendered when its father or mother is rendered, even when the state that triggered the father or mother to render has nothing to do with the binding itself. Instance beneath:
struct Father or mother: View {
@State var textual content = "textual content"
@State var bool = false
var physique: some View {
let _ = Self._printChanges()
VStack {
CustomBindingChild(worth: Binding(
get: {
textual content
},
set: { newValue in
textual content = newValue
}
))
BindingChild(worth: $textual content)
Button("Toggle bool") {
bool.toggle()
}
Button("Change textual content") {
textual content = textual content == "textual content" ? "textual content 2" : "textual content"
}
}
}
}
struct CustomBindingChild: View {
@Binding var worth: String
var physique: some View {
let _ = Self._printChanges()
Textual content(worth)
}
}
struct BindingChild: View {
@Binding var worth: String
var physique: some View {
let _ = Self._printChanges()
Textual content(worth)
}
}
With the setup above, in the event you faucet the “Change textual content” button all three views are printed, as I’d count on. In the event you faucet the “Toggle bool” button, then again, the father or mother view and the customized binding view are printed and I count on solely the father or mother to take action.
To avoid this problem I created my very own implementation of what I am calling a StableBinding
:
@propertyWrapper
struct StableBinding: Equatable {
typealias Getter = () -> Worth
typealias Setter = (Worth) -> Void
personal var getter: Getter
personal var setter: Setter
var wrappedValue: Worth {
get { return getter() }
nonmutating set { self.setter(newValue) }
}
var projectedValue: Self { self }
init(get: @escaping Getter, set: @escaping Setter) {
self.getter = get
self.setter = set
}
init(_ binding: Binding) {
self.getter = { binding.wrappedValue }
self.setter = { newValue in
binding.wrappedValue = newValue
}
}
static func == (lhs: StableBinding, rhs: StableBinding) -> Bool {
lhs.getter() == rhs.getter()
}
}
The truth that it implements the Equatable
protocol ensures that it does not trigger the view that is utilizing it to be rendered each time it is evaluated.
My questions are two:
- Is there a purpose behind Swift’s customized binding behaves like this?
- Is there an issue in my strategy? I’ve examined it and could not discover any however it will be actually disappointing to search out out that I missed one thing apparent after it hits manufacturing.