Skip to content

FB23722108: FetchDescriptor.propertiesToFetch is ignored; underlying SQL query always executes a full column SELECT statement. #823

Description

@malhal

Submission Date

2026-07-13

Status

Open

Area

SwiftData

Operating System Version

iOS 27 beta 3

Type

Incorrect/Unexpected Behavior

Description

Configuring propertiesToFetch on a FetchDescriptor has no effect on the generated SQL payload when executing a fetch pass via ModelContext.fetch(_:) or @Query.

When setting specific key paths in propertiesToFetch to optimize performance or memory layout (such as isolating a single String property for a sectioned layout or dynamic indexing pass), SwiftData silently overrides the optimization array. The underlying SQLite generation engine always expands the SELECT clause to include every single column mapped to the target entity, entirely defeating the utility of the public API property.

Steps to Reproduce
1 Create a basic @model class with multiple stored attributes.
2 Initialize a FetchDescriptor targeting that model.
3 Explicitly assign a subset of properties to descriptor.propertiesToFetch.
4 Run the fetch command via a ModelContext or bind it directly to a @query.
5 Observe the generated SQL trace output via the -com.apple.CoreData.SQLDebug 1 launch argument flag.

Example Implementation Code

@Model 
class Person {
    @Attribute(.unique) var id: String
    var name: String
    var age: Int
    
    init(id: String, name: String, age: Int) {
        self.id = id
        self.name = name
        self.age = age
    }
}

// Inside a fetch pass:
var fetchDescriptor = FetchDescriptor<Person>()
fetchDescriptor.propertiesToFetch = [\.name] // Intent: Only pull the 'name' column

let results = try modelContext.fetch(fetchDescriptor)

Expected Behavior
The database should execute an optimized projection query, fetching only the columns mapped to the explicitly specified key paths in the propertiesToFetch array:

SELECT t0.Z_PK, t0.ZNAME FROM ZPERSON t0

Actual Behavior
The array constraint is ignored. The resulting SQL execution traces a full row pull across all properties:

SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZID, t0.ZNAME, t0.ZAGE FROM ZPERSON t0

Diagnostic Context & Root Cause Analysis
This issue appears to stem from a systemic translation gap between SwiftData's API layer and legacy Core Data behaviors. In Core Data, an NSFetchRequest only alters its column SELECT parameters if the request type is switched to .dictionaryResultType. If the context returns full managed object instances (NSManagedObjectResultType), Core Data requires complete records or uniform faults, ignoring partial requests.

Because SwiftData’s ModelContext and @Query initializers natively enforce returning arrays of full concrete models ([T]), the underlying Core Data framework forces a full row schema retrieval.

If propertiesToFetch cannot alter the column mapping for concrete PersistentModel fetches due to graph integrity guarantees, the property should either be removed from public FetchDescriptor exposure or integrated with a dedicated raw/projection dictionary query mode to prevent deceptive execution performance expectations.

Keywords

No response

Prerequisites

  • The title follows the format FB<number>: <title>
  • I will keep this issue updated with Apple's responses

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions