EquatableView not forcing SwiftUI to use custom implementation of the == function
Equatable
is used when a view is created, so either it should replace already existed or not. In your example TestView
is not re-created, because it should not on binding changed - only body
rendered with new value.
To simulate recreation (for testing purpose) the simplest it to add condition. Equatibility is just a optimisation technic it helps SwiftUI to detect replace existed view instance or not.
Here is updated example to demo Equatable works. Tested with Xcode 12.1 / iOS 14.1
struct ContentView: View {
@State var state = false
var body: some View {
VStack {
if state {
EquatableView(content: TestView(state: $state))
}
Button("Change state", action: {state.toggle()})
}
}
}
struct TestView: View, Equatable {
@Binding var state: Bool
init(state: Binding<Bool>) {
_state = state
print(">> inited") // << check if created
}
var body: some View {
let _ = print("Test updated")
Text("TestView state : \(state.description)")
}
static func == (lhs: TestView, rhs: TestView) -> Bool {
//Never printed or invoked
let _ = print ("lhs == rhs invoked \(lhs.state) == \(rhs.state)")
return lhs.state == rhs.state
}
}
SwiftUI does not reliably propagate changes to child view
This is a result of your custom Equatable
conformance:
extension Book: Equatable {
static func == (lhs: Book, rhs: Book) -> Bool {
return lhs.id == rhs.id
}
}
If you remove this, it'll work as expected.
In your current code, you're saying that if two Book
s have the same ID, they are equal. My suspicion is that you don't actually mean that they are truly equal -- you just mean that they are the same book.
Your second option ("add && lhs.likes == rhs.likes
to the Equatable
conformance (which is not intended)") essentially just uses the synthesized Equatable
conformance that the system generates, since unusedProperty
isn't used -- so, if you were to use the second option, you may as well just remove the custom ==
function altogether.
I think the decision to make here is whether you really want to tell the system an untruth (that two Book
s, no matter what their other properties, are equal if they share the same id
) or if you should let the system do it's own work telling if the items are equal or not.
Observable object model not changing View values when model is updated
The main reason the card view doesn't see changes is because in your card view you did put an equatable conformance protocol where you specify an equality check ==
function that just checks for content and not other variable changes
static func ==(lhs: Game.Card, rhs: Game.Card) -> Bool {
lhs.content == rhs.content
// && lhs.isFaceUp && rhs.isFaceUp //<- you can still add this
}
if you remove the equatable protocol and leave swift to check for equality it should be the minimal change from your base solution.
I would still use the solution where you change the state of the class card so the view can react to changes as an ObservableObject
, and the @Published
for changes that the view need to track, like this:
class Card: Identifiable, Equatable, ObservableObject {
var id: Int
@Published var isFaceUp: Bool = false
var content: String
@Published var isMatchedUp: Bool = false
var isPreviouslySeen = false
internal init(id: Int, content: String) {
self.id = id
self.content = content
}
static func ==(lhs: Game.Card, rhs: Game.Card) -> Bool {
lhs.content == rhs.content
}
}
and in the Card
view the card variable will become
struct Card: View {
@ObservedObject var card: Game.Card
...
}
btw you don't need to notify the view of changes withobjectWillChange.send()
if you are already using the @Published
notation. every set to the variable will trigger an update.
Related Topics
Take Screenshot of Host App Using iOS Share/Action Extensions
Swift: Oslog/Os_Log Not Showing Up in Console App
Swift. Is the (Absolutely) Sole Specific Advantage of Unowned Over Weak, Performance
Pattern Matching in a Swift for Loop
How to Pass Binding to Child View in the New Navigationstack.Navigationdestination
Making a Http Post Request in Swift 2
Swift Lazy Stored Property Versus Regular Stored Property When Using Closure
Simple Observable Struct with Rxswift
Set Insets to the Collectionview Programmatically in Swift
How to Initialize a Mlmultiarray in Coreml
Convert String to Nsdate in Swift
Swiftui Pick a Value from a List with Ontap Gesture
Same Class Extension in Two Different Modules
Cannot Convert Value of Type 'Binding<String>' to Expected Argument Type 'Binding<String>'
Why Does My @Lazy Property Crash, But If I Make It Non Lazy It Works
Implement Protocol Through Extension
How to Wait for Http Requests to Finish
Use Quick Look Inside a Swift Cocoa Application to Preview Audio Files