Custom Equatable implementation prevents SwiftUI from updating its view

425 Views Asked by At

My model Keyword has the following implementation:

public struct Keyword: Codable, Equatable, Identifiable {
    
    public var id: String {
        name
    }
    public var name: String
    public var popularity: Int?
    
    public static func == (lhs: Keyword, rhs: Keyword) -> Bool {
        return lhs.name == rhs.name
    }

    [...]

KeywordsView has the following implementation:

@ObservedObject var viewModel = KeywordsViewModel()

var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.keywords) { keyword in
                    KeywordView(keyword: keyword)
                }

                [...]

When popularity is updated (e.g. using a web API), the view never gets updated. It works fine when I remove the static == method. It also works fine if I make this method always return false.

I have an idea why this is happening: SwiftUI probably (implicitly) uses == to figure out whether the view needs to be updated. And because popularity is left out of the equation, SwiftUI never updates the view although popularity is changed.

However, I would like to use the custom == implementation to make use of, amongst others, contains(_:) and compare instances of Keyword in my way.

How can I make this work?

https://developer.apple.com/documentation/swift/equatable

1

There are 1 best solutions below

2
dillon-mce On

One option to give you the benefits Equatable, without messing with SwiftUI's use of it would be to add some extensions that match your needs but use the identifier's equality instead. For example:

extension Sequence where Element: Identifiable {
    func contains(id: Element.ID) -> Bool {
        contains(where: { $0.id == id })
    }
}

extension Collection where Element: Identifiable {
    func firstIndex(of id: Element.ID) -> Index? {
        firstIndex(where: { $0.id == id })
    }
}

// keywords.contains(id: "some keyword")
// keywords.firstIndex(of: "some keyword")