Core Data
iOS 5
Data Import
Performance Optimization
iOS Development

Implementing Fast and Efficient Core Data Import on iOS 5

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Large Core Data imports on iOS 5 become slow when every record is inserted on the main context, every save is too frequent, or duplicate detection is done naïvely. The fast pattern is to import on a background context, save in batches, and minimize object creation and fetch churn.

Use a Background Managed Object Context

iOS 5 introduced better Core Data concurrency support, which makes private import contexts much more practical. The import work should not block the main thread.

objective-c
1NSManagedObjectContext *mainContext = self.managedObjectContext;
2
3NSManagedObjectContext *importContext =
4    [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
5importContext.parentContext = mainContext;
6
7[importContext performBlock:^{
8    // import work here
9}];

The key idea is that object creation, mapping, and deduplication happen on the private queue context. The UI stays responsive because the main context is not doing the heavy work record by record.

Save in Batches Instead of Per Record

Saving each object individually is expensive. It forces frequent validation and persistence work. A better approach is to insert a chunk, then save.

objective-c
1NSInteger count = 0;
2
3for (NSDictionary *row in incomingRows) {
4    MyEntity *entity = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity"
5                                                     inManagedObjectContext:importContext];
6    entity.identifier = row[@"id"];
7    entity.name = row[@"name"];
8    entity.updatedAt = row[@"updatedAt"];
9
10    count++;
11    if (count % 200 == 0) {
12        NSError *error = nil;
13        if (![importContext save:&error]) {
14            NSLog(@"Import save failed: %@", error);
15        }
16        [importContext reset];
17    }
18}

Batch saving reduces overhead, and reset releases registered objects so memory does not climb forever during a large import.

The exact batch size depends on the entity size and device limits, but the principle stays the same.

Avoid Duplicate Fetches One Row at a Time

The classic import bottleneck is fetching the database once per incoming row to check whether the object already exists. That becomes painfully slow.

A better strategy is to prefetch existing identifiers into a dictionary or set, then use that lookup during the import.

objective-c
1NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"MyEntity"];
2request.resultType = NSDictionaryResultType;
3request.propertiesToFetch = @[@"identifier"];
4
5NSError *fetchError = nil;
6NSArray *results = [importContext executeFetchRequest:request error:&fetchError];
7
8NSMutableSet *existingIds = [NSMutableSet set];
9for (NSDictionary *row in results) {
10    [existingIds addObject:row[@"identifier"]];
11}

Now the import loop can decide quickly whether a record should create a new object or update an existing one. That is much cheaper than repetitive per-row fetches.

If updates matter, you can also prefetch existing managed objects keyed by identifier, not just the ids themselves, as long as the data volume fits memory constraints.

Keep Mapping and Relationships Lean

During import, avoid unnecessary work in custom accessors, KVO-heavy patterns, or relationship traversals unless the import genuinely requires them. The import path should do only what is needed to construct valid persisted state.

Practical rules:

  • map primitive fields first
  • delay expensive relationship resolution when possible
  • keep validation predictable
  • avoid touching unrelated object graphs

For huge imports, even small extra work per record becomes noticeable.

Merge Back to the Main Context Carefully

If the import context is a child of the main context, saving the child is not the final persistence step. The main context must also save later.

objective-c
1[mainContext performBlock:^{
2    NSError *error = nil;
3    if (![mainContext save:&error]) {
4        NSLog(@"Main context save failed: %@", error);
5    }
6}];

That final save should usually happen at sensible checkpoints rather than after every small batch. The goal is still to avoid turning the main context into the bottleneck.

Common Pitfalls

  • Importing on the main context blocks UI and makes the app feel frozen.
  • Saving every object individually destroys throughput.
  • Fetching for duplicates one row at a time creates unnecessary database churn.
  • Never calling reset on a large import context can make memory usage balloon.
  • Updating large relationship graphs during raw import work often costs far more than expected.

Summary

  • Use a private-queue managed object context for import work on iOS 5.
  • Save in batches and reset the import context periodically to control memory.
  • Replace per-row duplicate fetches with preloaded lookup structures whenever possible.
  • Keep the import path lean and avoid unnecessary graph work.
  • Save changes back through the parent or main context in controlled checkpoints.

Course illustration
Course illustration

All Rights Reserved.