[ACCEPTED]-iOS CoreData batch insert?-ios

Accepted answer
Score: 31

Check out the Efficiently Importing Data chapter from the Core Data 10 Programming Guide.

I'm currently having the 9 same problems as you, only I'm inserting 8 10000 objects and it takes around 30s, which 7 is still slow for me. I'm doing a [managedObjectContext 6 save] on every 1000 managed objects inserted 5 into the context (in other words, my batch 4 size is 1000). I've experimented with 30 3 different batch sizes (from 1 to 10000), and 2 1000 seems to be the optimum value in my 1 case.

Score: 8

I was looking for the answer to a similar question when I 8 came across this one. @VladimirMitrovic's 7 answer was helpful at the time for knowing 6 that I shouldn't save the context every 5 time, but I was also looking for some sample 4 code.

Now that I have it, I will provide 3 the code below so that other people can 2 see what it might look like to do a batch 1 insert.

// set up a managed object context just for the insert. This is in addition to the managed object context you may have in your App Delegate.
let managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = (UIApplication.sharedApplication().delegate as! AppDelegate).persistentStoreCoordinator // or wherever your coordinator is

managedObjectContext.performBlock { // runs asynchronously

    while(true) { // loop through each batch of inserts. Your implementation may vary.

        autoreleasepool { // auto release objects after the batch save

            let array: Array<MyManagedObject>? = getNextBatchOfObjects() // The MyManagedObject class is your entity class, probably named the same as MyEntity
            if array == nil { break } // there are no more objects to insert so stop looping through the batches

            // insert new entity object
            for item in array! {
                let newEntityObject = NSEntityDescription.insertNewObjectForEntityForName("MyEntity", inManagedObjectContext: managedObjectContext) as! MyManagedObject
                newObject.attribute1 = item.whatever
                newObject.attribute2 = item.whoever
                newObject.attribute3 = item.whenever
            }
        }

        // only save once per batch insert
        do {
            try managedObjectContext.save()
        } catch {
            print(error)
        }

        managedObjectContext.reset()
    }
}
Score: 2

Swift 5.x

In 2022, consider using NSBatchInsertRequest to insert batch 4 data into persist store.

⚠️ Note: Please consider doing some tests before execute batch insert. For example, whether the container is empty or not. These tests may avoid possible data conflicts.

Create Batch Insert Request

Assume CustomMO is target 3 NSManagedObject, and here's code sample

// Load all data from JSON file
var jsonData: [CustomObjectData] = loadData()

func createBatchInsertRequest() -> NSBatchInsertRequest {
    // Create an iterator for raw data
    var itemListIterator = jsonData.makeIterator()
    
    let batchInserRequest = NSBatchInsertRequest(entity: CustomMO.entity()) { (obj: NSManagedObject) in
        // Stop add item when itemListIterator return nil
        guard let item = itemListIterator.next() else { return true }
        
        // Convert obj to CustomMO type and fill data to obj
        if let cmo = obj as? CustomMO {
            cmo.name = item.name
            cmo.description = item.description
        }
        
        // Continue add item to batch insert request
        return false
    }
    
    return batchInserRequest
}

Execute Request

You 2 can choose directly execute request or run 1 it in background

var container: NSPersistentContainer!
let request = createBatchInsertRequest()

// Directly Execute
do {
    try container.viewContext.execute(request)
} catch {
    // Log Error Here
}

// Execute In background
container.performBackgroundTask { context in
    do {
        try context.execute(request)
    } catch {
        // Log Error Here
    }
}

Extra Steps Refresh Row Cache

The code below from here

request.resultType = .objectIDs
let result = try! context.execute(request)
let resultInsert = result as? NSBatchInsertResult
if let objectIDs = resultInsert?.result as? [NSManagedObjectID], !objectIDs.isEmpty {
    let save = [NSInsertedObjectsKey: objectIDs]
    NSManagedObjectContext.mergeChanges(fromRemoteContextSave: save, into: [mainContext])
}
Score: 0

I like @Suragch 's answer very much. This 1 is the Objective-C version for it.

    NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];
    managedObjectContext.persistentStoreCoordinator = [[UIApplication sharedApplication].delegate.persistentStoreCoordinator];

    [managedObjectContext performBlock:^{
        while (true) {
            @autoreleasepool {
                // Code that creates autoreleased objects.
                NSArray *batchObjects = [self getNextBatchOfObjects];

                if (!batchObjects) {
                    break;
                }

                for (id item in batchObjects) {
                    MyEntity *newItem = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:managedObjectContext];
                    newObject.attribute1 = item.whatever;
                    newObject.attribute2 = item.whoever
                    newObject.attribute3 = item.whenever
                }
            }

            // only save once per batch insert
            NSError *error = nil;
            [managedObjectContext save:&error];
            [managedObjectContext reset];
        }
    }];

More Related questions