It’s been a while since I last worked on any mobile apps, so I thought the last couple weeks of sabbatical would be a good time to get caught up on iOS and specifically to learn Swift, which didn’t exist last time ?. For the most part, it was pretty easy to pick up and I was able to move fairly quickly on some apps I had been thinking about.
One of the only problems I ran into was related to Core Data concurrency. Specifically, if two different threads are reading and writing data, you get errors like 'NSGenericException', reason: Collection <__NSArrayM: 0x7fabb400> was mutated while being enumerated
.
The solution is private queue contexts. I won’t do a full explanation of queue contexts here, but the Apple Developer Documentation has some good information. The idea is to create a private context to operate on while we’re doing background work.
For this to work as expected there are a few things that need to happen:
- Set
context.parent
. In order for the changes to eventually be written to disk, we have to associate the new, private context with the main context by settingnewContext.parent = oldContext
. - Use
context.object(with: objectID)
to make core data relationships. We need to get a reference in the current context to any objects that were created outside the context. - After saving, we also need to save the parent context to commit the changes to disk. To make it thread safe, we use
oldContext.perform
oroldContext.performAndWait
depending on whether is should be asynchronous or not.
I put together a gist to demonstrate:
The following articles were especially helpful to understand how to fix this problem in my case: