The post gives us insight into how SwiftUI’s performance hinges on efficient view diffing—but the default behavior often falls short, especially with closures and complex view models. Manually writing Equatable conformances is tedious and error-prone.
An Equatable macro is introduced to solve this by generating optimized Equatable implementations for your SwiftUI views, ensuring accurate and performant diffing out of the box. It eliminates boilerplate, prevents regressions, and helps you avoid unnecessary re-renders—boosting UI performance while keeping your codebase clean and maintainable.
Ideal for teams scaling SwiftUI in large apps.
We at Ordo One decided to implement the macro and open-source it for the community.
Usage
Apply the @Equatable macro to structs to automatically generate Equatable conformance:
import Equatable
import SwiftUI
@Equatable
struct ProfileView: View {
var username: String // Will be compared
@State private var isLoading = false // Automatically skipped
@ObservedObject var viewModel: ProfileViewModel // Automatically skipped
@EquatableIgnored var cachedValue: String? // This property will be excluded
@EquatableIgnoredUnsafeClosure var onTap: () -> Void // This closure is safe and will be ignored in comparison (in order for it to be safe we must be sure that this closure does not capture value types on call site)
let id: UUID // Will be compared first for short-circuiting equality checks
var body: some View {
VStack {
Text(username)
if isLoading {
ProgressView()
}
}
}
}
The generated extension will implement the == operator with property comparisons ordered for optimal performance (e.g., IDs and simple types first):
Nice! Looking forward to taking this library for a spin.
Also: IIUC, it's also necessary to wrap a view in a EquatableView (or the shorthand equatable() modifier) so as to opt out of SwiftUI’s built-in reflection-based diffing.
I would suggest renaming this to EquatableView, both for clarity, and also because it becomes the obvious name if you add .equatable() using the macro.
This isn't necessary -- SwiftUI automatically uses an Equatable conformance if present rather than the default reflection-based comparison algorithm.
If a view is POD, then SwiftUI will compare the bits directly rather than using the Equatable conformance. However, POD views are pretty uncommon[1], and if your view is POD then you shouldn't be needing to use @EquatableIgnored in the view anyway.
Any SwiftUI property wrapper like @State makes a view not be POD, in addition to non-POD types like String, Array, Dictionary (all copy-on-write reference types under the hood), or any other reference type. Views without any of these are pretty uncommon and probably don't need to be optimized with @Equatable. ↩︎
We will be using this for non-SwiftUI use cases too, so such renaming would be misleading - it is clearly useful for SwiftUI - but there are other uses too…
Thanks for clarifying. Then I'm struggling to see a use case where one would reach for a EquatableView. This view appears to be a relic of the early SwiftUI days and seems ripe for deprecation...
If that's the case, then I think that while this library is focused only on Equatable, you unfortunately are forced to also support @Hashable. Otherwise it's possible to do something seemingly innocuous that is hiding a problem:
@Equatable
struct User: Hashable {
let id: Int
@EquatableIgnored
var name = ""
}