We have released two new default performance system actions for goal completion, as well as new custom actions that you can create and add yourself.
Data processing can be CPU-intensive, and if it is performed on the main queue, it can result in unresponsiveness in the UI. If you like ruining the UX of your app, then keep inserting or updating tons of objects on the main thread (which also controls the UI) every time time you make a
GET request. Otherwise, keep reading to learn how to knock concurrency out of the ballpark. ⚾
Your Core Data Stack Sucks
If you’re here, it’s probably because after a sleepless night of debugging, you realized your app is lagging because of inserting/updating many objects into Core Data. If you are reading this out of vigilance, then I must applaud you for being more proactive than I was. Either way, let’s learn how to make your stack not suck.
From a top-down view, we have
Persistent Store Coordinator - save object graphs to persistent storage and to retrieve model information. In other words, it takes stuff added in the main context and writes it to disk, hence “persistent”
Main Context - This will be the main context you use in your app. You access this context on your main thread.
Private Context (Secret Weapon) - When inserting/updating a large amount of objects, you will want to operate in this context. When creating a managed object context with a concurrency type of NSPrivateQueueConcurrencyType, the managed object context is associated with a private queue and it MUST be accessed from that private queue via a
perform block. Have fun debugging if you ignore this pro-tip.
Removing Lag One Thread at a Time
Now that we’ve gone over the basics, it’s time for you to copy and paste some code…I mean, diligently read it over…
[gist https://gist.github.com/michael-mckenna/95aa876997315bcdd85dc1337677985e /]
So where does the magic happen? Right there in the initializations of the managed object contexts. We set the
mainObjectContext to have a coordinator as it is responsible for writing changes from memory to disk upon saving. The
privateManagedObjectContext operates on a background thread, as noted by its concurrency type.
So far we learned how to make your Core Data stack not suck, and you got some code you can copy and paste. Let me add one more item to that list.
Let’s say you want to insert or update a lot of users you got in a network response. You will want to check if the user exists locally and update it with the corresponding user in the response, else insert a new user. Let’s add a search method.
[gist https://gist.github.com/michael-mckenna/6b09529e529c56aa851d180cc50e4afb /]
It is important to note that we are specifying the context to perform the fetch request. If we were to fetch objects in the
mainObjectContext then proceed to insert them into the
privateManagedObjectContext, you’ll be greeted with a crash.
Home Run Time
The hard part is finished. Now let’s combine everything we have so far!
[gist https://gist.github.com/michael-mckenna/2ace7ac25fbcb5c0d69f84c6b23b8a4a /]
IMPORTANT! Ensures private context changes are performed on the proper thread. Without this perform block, as mentioned earlier, you’ll run into errors.
Loop through each user in the array of user’s in the JSON response
Query for user. If the user exists, update the local user with JSON, else insert a new user.
THE ORDER IS IMPORTANT! Save the private context, which pushes changes to the main context without blocking the main queue (thus preventing UI Lag). Finally, the object is written to disk.
To help with debugging concurrency issues, go to Product -> Scheme -> Edit Scheme then add the following arguments:
Set up a core data stack two have two managed object contexts, where the main object context has a persistent store coordinator and a private managed object context that has a parent context which is the main object context.
Set up a query method to query core data on the proper thread
Perform inserts and updates on the private context when inserting/updating lots of objects