Reputation: 5003
I have the following core data model:
I have a view controller that contains a particular SkillGroup. I would like to make a fetch request that contains the records for all skills in that skillGroup. Normally I would think it would be something like
request.predicate = NSPredicate(format: "skill IN %@.skills", skillGroup!)
However I needed to create the intermediate "SkillGroupItem" models so that I could keep track of the displayIndex for each skill in a skillGroup. I am thinking of something like:
request.predicate = NSPredicate(format: "ANY %@.skillGroupItems.skill = skillProgress.skill", skillGroup!)
so any of the skillGroup's skillGroupItems' skill is equal to the records skillProgress object.skill...
but that throws this error:
CoreData: annotation: to-many relationship fault "skillGroupItems" for objectID 0xb9291ca50355a7c1 <x-coredata://33325602-3973-46E7-8400-45922DD97A05/SkillGroup/p1> fulfilled from database. Got 1 rows
CoreData: sql: SELECT DISTINCT t0.Z_ENT, t0.Z_PK, t0.Z_OPT, t0.ZRECORDTYPE, t0.ZTIMESTAMP, t0.ZSKILLPROGRESS, t0.ZIMAGEFILENAME, t0.ZTEXT, t0.ZTHUMBNAILFILENAME, t0.ZVIDEOFILENAME FROM ZRECORD t0 JOIN ZSKILLPROGRESS t1 ON t0.ZSKILLPROGRESS = t1.Z_PK WHERE ? = t1.ZSKILL ORDER BY t0.ZTIMESTAMP
2021-04-20 13:37:43.482875-0700 SweatNetOffline[38167:3014868] -[__NSSingleObjectSetI longLongValue]: unrecognized selector sent to instance 0x600002a69360
CoreData: annotation: total fetch execution time: 0.0013s for 0 rows.
2021-04-20 13:37:43.484519-0700 SweatNetOffline[38167:3014868] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLFetchRequestContext: 0x60000117d5e0> , -[__NSSingleObjectSetI longLongValue]: unrecognized selector sent to instance 0x600002a69360 with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLFetchRequestContext: 0x60000117d5e0> , -[__NSSingleObjectSetI longLongValue]: unrecognized selector sent to instance 0x600002a69360 with userInfo of (null)
2021-04-20 13:37:43.503515-0700 SweatNetOffline[38167:3014868] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSSingleObjectSetI longLongValue]: unrecognized selector sent to instance 0x600002a69360'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff20421af6 __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007fff20177e78 objc_exception_throw + 48
2 CoreFoundation 0x00007fff204306f7 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
3 CoreFoundation 0x00007fff20426036 ___forwarding___ + 1489
4 CoreFoundation 0x00007fff20428068 _CF_forwarding_prep_0 + 120
5 CoreData 0x00007fff25114346 -[NSSQLiteConnection execute] + 1416
6 CoreData 0x00007fff2538acfa _newFetchedRowsForFetchPlan_ST + 1234
7 CoreData 0x00007fff2537b1c5 _executeFetchRequest + 55
8 CoreData 0x00007fff252cd352 -[NSSQLFetchRequestContext executeRequestCore:] + 41
9 CoreData 0x00007fff253430a3 -[NSSQLStoreRequestContext executeRequestUsingConnection:] + 405
10 CoreData 0x00007fff2531468f __52-[NSSQLDefaultConnectionManager handleStoreRequest:]_block_invoke + 56
11 CoreData 0x00007fff2527bdbd __37-[NSSQLiteConnection performAndWait:]_block_invoke + 28
12 libdispatch.dylib 0x00000001094479c8 _dispatch_client_callout + 8
13 libdispatch.dylib 0x0000000109456bfe _dispatch_lane_barrier_sync_invoke_and_complete + 132
14 CoreData 0x00007fff2527bca3 -[NSSQLiteConnection performAndWait:] + 134
15 CoreData 0x00007fff253145a4 -[NSSQLDefaultConnectionManager handleStoreRequest:] + 273
16 CoreData 0x00007fff2531afd8 -[NSSQLCoreDispatchManager routeStoreRequest:] + 283
17 CoreData 0x00007fff2524b284 -[NSSQLCore dispatchRequest:withRetries:] + 161
18 CoreData 0x00007fff2524681e -[NSSQLCore processFetchRequest:inContext:] + 88
19 CoreData 0x00007fff2511a461 -[NSSQLCore executeRequest:withContext:error:] + 1072
20 CoreData 0x00007fff2522595a __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke.797 + 3219
21 CoreData 0x00007fff2521e02a -[NSPersistentStoreCoordinator _routeHeavyweightBlock:] + 222
22 CoreData 0x00007fff2511993e -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 1684
23 CoreData 0x00007fff25117ef2 -[NSManagedObjectContext executeFetchRequest:error:] + 885
24 CoreData 0x00007fff252da7d0 __43-[NSFetchedResultsController performFetch:]_block_invoke + 417
25 CoreData 0x00007fff2514ce63 developerSubmittedBlockToNSManagedObjectContextPerform + 154
26 CoreData 0x00007fff2514cd4a -[NSManagedObjectContext performBlockAndWait:] + 197
27 CoreData 0x00007fff252dcc13 -[NSFetchedResultsController _recursivePerformBlockAndWait:withContext:] + 145
28 CoreData 0x00007fff252da523 -[NSFetchedResultsController performFetch:] + 231
29 SweatNetOffline 0x0000000108ece722 $s15SweatNetOffline22ProgressViewControllerC11viewDidLoadyyF + 1122
30 SweatNetOffline 0x0000000108ecf8db $s15SweatNetOffline22ProgressViewControllerC11viewDidLoadyyFTo + 43
31 UIKitCore 0x00007fff23f5e36e -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 88
32 UIKitCore 0x00007fff23f62cd7 -[UIViewController loadViewIfRequired] + 1084
33 UIKitCore 0x00007fff23f630c1 -[UIViewController view] + 27
34 UIKitCore 0x00007fff23e82c37 -[UINavigationController _startCustomTransition:] + 1254
35 UIKitCore 0x00007fff23e991d6 -[UINavigationController _startDeferredTransitionIfNeeded:] + 684
36 UIKitCore 0x00007fff23e9a5e8 -[UINavigationController __viewWillLayoutSubviews] + 150
37 UIKitCore 0x00007fff23e7ad9e -[UILayoutContainerView layoutSubviews] + 217
38 UIKitCore 0x00007fff24bf8504 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2924
39 QuartzCore 0x00007fff27b1bc2b -[CALayer layoutSublayers] + 258
40 QuartzCore 0x00007fff27b2219d _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 575
41 QuartzCore 0x00007fff27b2df3f _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 65
42 QuartzCore 0x00007fff27a6d44c _ZN2CA7Context18commit_transactionEPNS_11TransactionEdPd + 496
43 QuartzCore 0x00007fff27aa4233 _ZN2CA11Transaction6commitEv + 783
44 QuartzCore 0x00007fff27aa53ef _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 79
45 CoreFoundation 0x00007fff2038f1f8 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
46 CoreFoundation 0x00007fff20389a77 __CFRunLoopDoObservers + 547
47 CoreFoundation 0x00007fff2038a01a __CFRunLoopRun + 1113
48 CoreFoundation 0x00007fff203896d6 CFRunLoopRunSpecific + 567
49 GraphicsServices 0x00007fff2c257db3 GSEventRunModal + 139
50 UIKitCore 0x00007fff24696cf7 -[UIApplication _run] + 912
51 UIKitCore 0x00007fff2469bba8 UIApplicationMain + 101
52 SweatNetOffline 0x0000000108e3d9fb main + 75
53 libdyld.dylib 0x00007fff2025a3e9 start + 1
54 ??? 0x0000000000000003 0x0 + 3
)
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSSingleObjectSetI longLongValue]: unrecognized selector sent to instance 0x600002a69360'
terminating with uncaught exception of type NSException
CoreSimulator 732.18.6 - Device: iPhone 11 (E5E1A675-AA38-45CE-A047-1C5569598381) - Runtime: iOS 14.4 (18D46) - DeviceType: iPhone 11
Im tripped up by how to use the ANY in this case. so any of the skillGroupItems makes sense.. but ANY skillGroup.skillGroupItems.skill seems bad to me. You can't look for a .skill on an array of skillGroupItems.
Any advice on how to accomplish this? Thanks
Upvotes: 1
Views: 138
Reputation: 21536
The predicate is parsed using the entity you are fetching (in your case, Record
) as the reference point. So you would use this:
request.predicate = NSPredicate(format: "recordType == %@", givenRecordType)
to create a predicate based on the attribute recordType
, and
request.predicate = NSPredicate(format: "skillProgress == %@", givenSkillProgressObject)
to create a predicate based on the relationship skillProgress
(if givenSkillProgress
was a SkillProgress
object you already had fetched). If you do not have a SkillProgress
object in memory, but you know an attribute value you want to match for it, you can use the corresponding keypath expression to refer to the attribute of the related object, eg:
request.predicate = NSPredicate(format: "skillProgress.isActive == YES")
will fetch those Record
objects whose related SkillProgress
object is active. You can continue to "traverse" the relationship tree by using additional elements in the key path. So:
request.predicate = NSPredicate(format: "skillProgress.skill.id == %@", givenSkillID)
will fetch those Record
objects whose related SkillProgress
is in turn related to a Skill
whose id
attribute matches the givenSkillID
. For the skillGroupItems
relationship (on the Skill
entity), because it is to-many, you will need to use "ANY" to indicate that any of the related objects should match your criteria:
request.predicate = NSPredicate(format: "ANY skillProgress.skill.skillGroupItems == %@", givenSkillGroupItem)
The final step is then to use the skillGroup
relationship from SkillGroupItem
to get the full key path from Record
to SkillGroup
:
request.predicate = NSPredicate(format: "ANY skillProgress.skill.skillGroupItems.skillGroup == %@", givenSkillGroup)
Upvotes: 1