Coredata is very slow; swift
If you have many objects of the same type, Core Data is naturally going to take a long time to save things. Doesn't matter what you do, it will take a while. What you can do is to configure the save to take places in a background thread as to not freeze your UI.
The easiest way is to create a background context for your main context. The idea is that your main context saves its data to the parent context, and the parent context is in charge of persisting data to disk. Basically like this:
Because both your main context and parent context are running in memory, the save operation from the child to the parent is fast. Once the parent has your location objects, it will save in a background thread. It may still take a long time, but at the very least it should not freeze your UI.
And you can configure this in your code like this:
lazy var parentContext: NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType:.PrivateQueueConcurrencyType)
moc.persistentStoreCoordinator = self.coordinator
return moc
}()
lazy var context: NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType:.MainQueueConcurrencyType)
// moc.persistentStoreCoordinator = self.coordinator
moc.parentContext = self.parentContext
return moc
}()
context
will be the child context. You can see how easy it is to give it a parent context.
Then, to save your data:
class func save(moc:NSManagedObjectContext) {
moc.performBlockAndWait {
if moc.hasChanges {
do {
try moc.save()
} catch {
print("ERROR saving context \(moc.description) - \(error)")
}
}
if let parentContext = moc.parentContext {
save(parentContext)
}
}
}
(Code taken and slightly edited from the "Learning Core Data for iOS with Swift: A Hands-on Guide" book by Tim Roadley).
Swift CoreData: accessing relationship is slower than just fetching by predicate - is it designed that way?
Accessing via the relationship, then sorting and discarding 4950 / 5000 items means that all of that processing has to be done in memory. Using fetch requests means that you can get the database to do the work.
If you have a relationship between the library and its books, you can use that in a predicate. In your code above, that would be something like:
let predicate = NSPredicate(format: "library == %@", library)
That will likely be faster than storing the ID, unless you have an index added on library ID.
Core data slow processing updates on a background thread
I hit similar slow performance when upserting a large collection of objects. In my case I'm willing to keep the full change set in memory and perform a single save so the large volume of fetch requests dominated my processing time.
I got a significant performance improvement from maintaining an in memory cache mapping my resources' primary keys to NSManagedObjectID
s. That allowed me to use existingObjectWithId:error:
rather than a fetch request for an individual object.
I suspect I might do even better by collecting the primary keys for all resources of a given entity description, issuing a single fetch request for all of them at once (batching those results as necessary), and then processing the changes to each resource.
Core data save takes too long
How big are these objects? How much data are you trying to save?
What type of persistent store are you using? SQLite?
What is your UI doing with this data after you save? Are you doing any kind of parsing of the data or display of the data?
What does Instruments say? Where does it say the time is being spent?
These are the first questions.
Core Data in no way should be taking that long to save the data. I suspect you are doing something in your User Interface that is being triggered by the save that is really taking the time. Time Profiler in instruments will tell us what is going on.
While I would not write this the way you are doing it, there is nothing wrong with the code you posted therefore the issue must be somewhere else. Instruments will tell us where to look.
Update
Thank you for posting the trace, it confirms my suspicions. If you look at where the time is being spent, 54% is in your NSFetchedResultsController
and 20.6% is being spent in your TimelineViewController
. What this means is that the save is very quick and then your UI is reacting very slowly to the changes. Turn off the merge changes notification to confirm. From there I would suggest digging deeper into your UI in time profiler and find out why it is so incredibly slow.
Looks like you are doing some complex string manipulation and other things in there that are very CPU intensive. Fix those and the speed will come back.
Related Topics
Correct Way to Layout Swiftui (Similar to Autolayout)
Uitableview Inside Uitableviewcell with Dynamic Height
Save Image with the Correct Orientation - Swift & Core Image
Calculate Distance Between My Location and a Mapkit Pin on Swift
Navigation with Only Back Button and Transparent Background
Alpha for Background Color Not Working in iOS
Blur Effect - Background Uitextfield
Changing the Shape of a Uiview Border
How to Read Heart Rate from iOS Healthkit App Using Swift
Turning on Thread Sanitizer Results in Signal Sigabrt
Return in Function Without Return Value in Swift
How to Convert Data Dictionary into an Array Swift
How to Call Presentviewcontroller in Uiview Class