-
Notifications
You must be signed in to change notification settings - Fork 11
Description
Issue
The @Equatable macro is incompatible with SwiftData's @Query property wrapper, preventing its use in views that need to fetch and observe persistent data.
Reproduction
import Equatable
import SwiftData
import SwiftUI
@Model
final class Item {
var name: String
var timestamp: Date
init(name: String, timestamp: Date = .now) {
self.name = name
self.timestamp = timestamp
}
}
@Equatable(isolation: .main)
struct ItemListView: View {
@Query private var items: [Item]
let filterText: String
var body: some View {
List(items) { item in
Text(item.name)
}
}
}Error
When the view is rendered:
Set a .modelContext in view's environment to use Query
This occurs even when modelContext is properly provided via .environment(\.modelContext, modelContext) or .modelContainer().
Root Cause
The @Equatable macro generates a custom init() that runs during view initialization. However, SwiftUI's @Query property wrapper depends on EnvironmentValues.modelContext being available during initialization to set up its fetch request and model observation.
The initialization timing conflict occurs because:
@Equatablegenerates custom initialization code that executes first- SwiftUI's environment injection happens after the view struct is initialized
@Queryattempts to accessmodelContextfrom the environment during its property wrapper initialization- The environment hasn't been injected yet, causing the runtime error
This is similar to other SwiftUI property wrappers that depend on environment values (like @FetchRequest in Core Data), where custom initialization can break the environment dependency chain.
Expected Behavior
Ideally, @Equatable should be compatible with all SwiftUI property wrappers, including those that depend on environment values like @Query, @FetchRequest, and custom @Environment properties.
Current Workaround
Views that use @Query cannot use @Equatable. Instead, child components that don't use @Query can be optimized:
// Parent view - cannot use @Equatable
struct ItemListView: View {
@Query private var items: [Item]
let filterText: String
var body: some View {
List(items) { item in
ItemRow(item: item) // This can use @Equatable
}
}
}
// Child component - can use @Equatable
@Equatable(isolation: .main)
struct ItemRow: View {
let item: Item
var body: some View {
Text(item.name)
}
}Related Apple Documentation
Environment
- Swift 6.2
- SwiftUI (iOS 17.0+)
- SwiftData (iOS 17.0+)
- Equatable v1.2.0
Potential Solution
The macro might need to detect SwiftUI property wrappers that depend on environment values and either:
- Skip custom init generation for views with environment-dependent property wrappers
- Generate init code that preserves environment injection timing
- Provide a compilation error with a helpful message when incompatible property wrappers are detected