SwiftData: Crash when deleting from model, but only in prod

I'm testing my app before releasing to testers, and my app (both macOS and iOS) is crashing when I perform one operation, but only in the production build.

I have data that loads from a remote source, and can be periodically updated. There is an option to delete all of that data from the iCloud data store, unless the user has modified a record. Each table has a flag to indicate that (userEdited). Here's the function that is crashing:

func deleteCommonData<T:PersistentModel & SDBuddyModel>(_ type: T.Type) throws {
    try modelContext.delete(model: T.self, where: #Predicate<T> { !$0.userEdited })
}

Here's one of the calls that results in a crash:

try modelManager.deleteCommonData(Link.self)

Here's the error from iOS Console:

SwiftData/DataUtilities.swift:85: Fatal error: Couldn't find \Link.<computed 0x0000000104b9d208 (Bool)> on Link with fields [SwiftData.Schema.PropertyMetadata(name: "id", keypath: \Link.<computed 0x0000000104b09b44 (String)>, defaultValue: Optional("54EC6602-CA7C-4EC7-AC06-16E7F2E22DE7"), metadata: nil), SwiftData.Schema.PropertyMetadata(name: "name", keypath: \Link.<computed 0x0000000104b09b84 (String)>, defaultValue: Optional(""), metadata: nil), SwiftData.Schema.PropertyMetadata(name: "url", keypath: \Link.<computed 0x0000000104b09bc4 (String)>, defaultValue: Optional(""), metadata: nil), SwiftData.Schema.PropertyMetadata(name: "desc", keypath: \Link.<computed 0x0000000104b09c04 (String)>, defaultValue: Optional(""), metadata: nil), SwiftData.Schema.PropertyMetadata(name: "userEdited", keypath: \Link.<computed 0x0000000104b09664 (Bool)>, defaultValue: Optional(false), metadata: nil), SwiftData.Schema.PropertyMetadata(name: "modified", keypath: \Link.<computed 0x0000000104b09c44 (Date)>, defaultVal<…>

Here's a fragment of the crash log:

Exception Type:    EXC_BREAKPOINT (SIGTRAP)
Exception Codes:   0x0000000000000001, 0x000000019373222c

Termination Reason:  Namespace SIGNAL, Code 5, Trace/BPT trap: 5
Terminating Process: exc handler [80543]


Thread 0 Crashed:
0   libswiftCore.dylib            	       0x19373222c _assertionFailure(_:_:file:line:flags:) + 176
1   SwiftData                     	       0x22a222160 0x22a1ad000 + 479584
2   SwiftData                     	       0x22a2709c0 0x22a1ad000 + 801216
3   SwiftData                     	       0x22a221b08 0x22a1ad000 + 477960
4   SwiftData                     	       0x22a27b0ec 0x22a1ad000 + 844012
5   SwiftData                     	       0x22a27b084 0x22a1ad000 + 843908
6   SwiftData                     	       0x22a28182c 0x22a1ad000 + 870444
7   SwiftData                     	       0x22a2809e8 0x22a1ad000 + 866792
8   SwiftData                     	       0x22a285204 0x22a1ad000 + 885252
9   SwiftData                     	       0x22a281c7c 0x22a1ad000 + 871548
10  SwiftData                     	       0x22a27cf6c 0x22a1ad000 + 851820
11  SwiftData                     	       0x22a27cc48 0x22a1ad000 + 851016
12  SwiftData                     	       0x22a27a6b0 0x22a1ad000 + 841392
13  SwiftData                     	       0x22a285b2c 0x22a1ad000 + 887596
14  SwiftData                     	       0x22a285a10 0x22a1ad000 + 887312
15  SwiftData                     	       0x22a285bcc 0x22a1ad000 + 887756
16  SwiftData                     	       0x22a27cf6c 0x22a1ad000 + 851820
17  SwiftData                     	       0x22a27cc48 0x22a1ad000 + 851016
18  SwiftData                     	       0x22a27a6b0 0x22a1ad000 + 841392
19  SwiftData                     	       0x22a27c0d8 0x22a1ad000 + 848088
20  SwiftData                     	       0x22a27a654 0x22a1ad000 + 841300
21  SwiftData                     	       0x22a1be548 0x22a1ad000 + 70984
22  SwiftData                     	       0x22a1cfd64 0x22a1ad000 + 142692
23  SwiftData                     	       0x22a1b9618 0x22a1ad000 + 50712
24  SwiftData                     	       0x22a1d2e8c 0x22a1ad000 + 155276
25  CoreData                      	       0x187fbb568 thunk for @callee_guaranteed () -> (@out A, @error @owned Error) + 28
26  CoreData                      	       0x187fc2300 partial apply for thunk for @callee_guaranteed () -> (@out A, @error @owned Error) + 24
27  CoreData                      	       0x187fc19c4 closure #1 in closure #1 in NSManagedObjectContext._rethrowsHelper_performAndWait<A>(fn:execute:rescue:) + 192
28  CoreData                      	       0x187fbbda8 thunk for @callee_guaranteed @Sendable () -> () + 28
29  CoreData                      	       0x187fbbdd0 thunk for @escaping @callee_guaranteed @Sendable () -> () + 28
30  CoreData                      	       0x187f663fc developerSubmittedBlockToNSManagedObjectContextPerform + 252
31  libdispatch.dylib             	       0x180336ac4 _dispatch_client_callout + 16
32  libdispatch.dylib             	       0x18032c940 _dispatch_lane_barrier_sync_invoke_and_complete + 56
33  CoreData                      	       0x187fd7290 -[NSManagedObjectContext performBlockAndWait:] + 364
34  CoreData                      	       0x187fc1fb8 NSManagedObjectContext.performAndWait<A>(_:) + 544
35  SwiftData                     	       0x22a1b877c 0x22a1ad000 + 46972
36  SwiftData                     	       0x22a1be2a8 0x22a1ad000 + 70312
37  SwiftData                     	       0x22a1c0e34 0x22a1ad000 + 81460
38  SwiftData                     	       0x22a23ea94 0x22a1ad000 + 596628
39  SwiftData                     	       0x22a256828 0x22a1ad000 + 694312
40  Sourdough Buddy               	       0x104e5dc98 specialized ModelManager.deleteCommonData<A>(_:) + 144 (ModelManager.swift:128) [inlined]
41  Sourdough Buddy               	       0x104e5dc98 closure #1 in SettingsView.clearStarterData.getter + 876 (SettingsView.swift:243)

It works if I do the following instead:

try modelContext.delete(model: Link.self, where: #Predicate { !$0.userEdited })

Why would the func call work in development, but crash in production? And why does doing the more verbose way work instead?

I think this is a bug.

Thanks

What do you mean with "more verbose way"? It seems to be the same command but one is wrapped in a generic function.

What do you mean with "more verbose way"? It seems to be the same command but one is wrapped in a generic function.

That's exactly what I mean. I'm deleting data from 5 different tables. The function call promotes code reuse so I don't have to declare the predicate 5 times, once for each table.

Yeah, this is kind of a known issue, as mentioned here.

You might consider using a concrete type, as opposed to a generic type, to construct the key path.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

SwiftData: Crash when deleting from model, but only in prod
 
 
Q