I have a table, which uses a NSFetchedResultsController to populate it's data. When I refresh my table, I need to update all 50+ items, so I do the following: I make a call to the server which returns JSON data, store the "media" object into an array, loop through this array and individually store each object to core data (in background thread), then reload the table. This works fine. However there is a major issue.
Sometimes the step of saving to the database takes 7+ seconds, due to looping through large arrays and individually storing each object to core data. And while this step is executing, when I fetch other data from the server, the response time is delayed tremendously. I wont be able to fetch new data until the save process is complete. I'm quite confused because this is supposed to be done in the background thread and not block other server calls.
Why does saving data to core data in bg causing my response time to be delayed? Is there a better approach to storing large arrays to core data without disrupting any responses?
//Refreshing User Table method
class func refreshUserProfileTable(callback: (error: NSError?) -> Void) {
//getProfile fetches data from server
ProfileWSFacade.getProfile(RequestManager.userID()!) {
(profile, isLastPage, error) -> () in
DataBaseManager.sharedInstance.saveInBackground({ (backgroundContext) in
let mediaList = profile?["media"] as? Array<JSONDictionary>
if let mediaList = mediaList {
//Response time is delayed when this loop is executing
for media in mediaList {
DataBaseManager.sharedInstance.storeObjectOfClass(Media.self, dict: media, context: backgroundContext)
}
}
}, completion: {
callback(error: error)
})
}
}
//MARK: Core data methods:
//Save in background method in Database manager
func saveInBackground(
block: (backgroundContext: NSManagedObjectContext) -> Void,
completion: (Void->Void)? = nil)
{
let mainThreadCompletion = {
if let completion = completion {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion()
})
}
}
backgroundContext.performBlock { () -> Void in
guard RequestManager.userID() != nil else {
mainThreadCompletion()
return
}
block(backgroundContext: self.backgroundContext)
if RequestManager.userID() != nil {
_ = try? self.backgroundContext.save()
DataBaseManager.sharedInstance.save()
}
mainThreadCompletion()
}
}
//Stores class object
func storeObjectOfClass<T: NSManagedObject where T: Mappable>(
entityClass:T.Type,
dict: JSONDictionary,
context: NSManagedObjectContext? = nil) -> T
{
let context = context ?? mainManagedObjectContext
let predicate = NSPredicate(format: "%K LIKE %@", entityClass.primaryKey(), entityClass.primaryKeyFromDict(dict))
let requestedObject = DataBaseManager.createOrUpdateFirstEntity(
entityType: T.self,
predicate: predicate,
context: context) { (entity) -> () in
entity.populateFromDictionary(dict)
}
return requestedObject
}
//Creates or updates core data entity
class func createOrUpdateFirstEntity<T: NSManagedObject>(
entityType entityType: T.Type,
predicate: NSPredicate,
context: NSManagedObjectContext,
entityUpdateBlock:(entity: T) -> ()) -> T
{
guard DataBaseManager.sharedInstance.doPersistentStoreAvailible() else { return T() }
let desc = NSEntityDescription.entityForName(String(entityType), inManagedObjectContext: context)!
let existingEntityRequest = NSFetchRequest()
existingEntityRequest.entity = desc
existingEntityRequest.predicate = predicate
let requestedObject = try? context.executeFetchRequest(existingEntityRequest).first
if let requestedObject = requestedObject as? T {
entityUpdateBlock(entity: requestedObject)
return requestedObject
} else {
let newObject = T(entity: desc, insertIntoManagedObjectContext: context)
entityUpdateBlock(entity: newObject)
return newObject
}
}