You're hitting some "I can't see your code, so I can't see what horrible misconception you implemented" limits. :-/
1. Because of how the runtime works, the first few dozen stack frames of the main thread are just internal methods with confusing names.
2. If something goes wrong, the stuff in the stack above where the debugger stopped often ends up being more confusing internal names. It's like crashing a car--where it stops isn't always related to what blew out. 😮
As as far as the CoreData stuff goes, there have been a few realignments and paradigm changes in "How are you supposed to structure Core Data stacks?" over the years.
- First came manual threads and thread containment (in the iOS 3 era)
- Later came NSManagedDocument and "merge changes and save in the background"
- Now we've got NSPersistentContainer and its independent parallel contexts.
If you read advice on the internet, you have to know which "era" (not the right word, but...) it's from and whether you're working on code from the same era. The code structures used are different in each of the eras.
It's also worth knowing that the thread/dispatch queue system has a lot of optimization in it to avoid switching if possible. Short version is that sometimes stuff works in spite of it being wrong because the execution got squeezed together in a fortunate ordering. But on a different device (or under different conditions) execution happens in a different order and things **** up.