Core Data
Data Migration
Multiple Passes
iOS Development
Data Management

Example or explanation of Core Data Migration with multiple passes?

Master System Design with Codemia

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

Introduction

A multi-pass Core Data migration means you do not jump directly from the oldest store model to the newest one. Instead, you migrate through one or more intermediate model versions, which is useful when the schema changes are too complex for one inferred mapping or when a large refactor is easier to express as smaller transformations.

Why Multiple Passes Exist

Core Data can do lightweight migration automatically when the model changes are simple. That works well for adding attributes, renaming with proper identifiers, or making compatible structural changes.

The trouble starts when a single upgrade needs several conceptual transformations, such as:

  • splitting one entity into two
  • replacing one attribute with several derived attributes
  • changing relationships and cardinality in the same release

In those cases, one giant migration can become difficult to reason about. Multiple passes let you break it into smaller steps.

The High-Level Pattern

Suppose the data model versions are:

  • 'ModelV1'
  • 'ModelV2'
  • 'ModelV3'

Instead of migrating V1 straight to V3, you do:

  1. migrate V1 to V2
  2. migrate the new store from V2 to V3

Each pass has its own source model, destination model, and mapping model, whether inferred or custom.

This is especially useful when one intermediate model exists only to make the data reshaping manageable.

A Conceptual Example

Imagine Person in V1 has one attribute called fullName. In V2, you split that into firstName and lastName. Then in V3, you introduce a separate Job entity and connect Person to it.

Doing both changes in one heavy migration is possible, but it is often easier to reason about them separately:

  • pass one: split fullName
  • pass two: move job-related data into the new relationship

That is the kind of situation where multi-pass migration earns its complexity.

A Swift Example of Sequential Migration

The code below sketches the orchestration logic. The exact mapping-model details depend on your app, but the flow is the important part.

swift
1import CoreData
2
3func migrateStore(
4    storeURL: URL,
5    modelPairs: [(source: NSManagedObjectModel, destination: NSManagedObjectModel, mapping: NSMappingModel)]
6) throws {
7    var currentURL = storeURL
8
9    for (index, pair) in modelPairs.enumerated() {
10        let manager = NSMigrationManager(sourceModel: pair.source, destinationModel: pair.destination)
11        let nextURL = storeURL.deletingLastPathComponent().appendingPathComponent("migration-step-\(index).sqlite")
12
13        try manager.migrateStore(
14            from: currentURL,
15            sourceType: NSSQLiteStoreType,
16            options: nil,
17            with: pair.mapping,
18            toDestinationURL: nextURL,
19            destinationType: NSSQLiteStoreType,
20            destinationOptions: nil
21        )
22
23        currentURL = nextURL
24    }
25}

In a real application, you would usually replace the original store with the fully migrated final store after the last pass succeeds.

How Mapping Models Fit In

Each migration pass can use either:

  • an inferred mapping model, if the change is simple enough
  • a custom mapping model, if you need explicit transform logic

If one pass is lightweight and another is heavyweight, that is fine. Multi-pass migration is compatible with mixed migration styles.

The point is not that every pass must be custom. The point is that every pass should be understandable and testable.

Testing Matters More Than the Orchestration Code

The hardest part of multi-pass migration is not the loop over versions. It is validating that every intermediate result is correct.

A sound test strategy includes:

  • seed stores created with older model versions
  • assertions after each migration pass, not only at the end
  • edge-case records that exercise renamed attributes, missing values, and relationship rewrites

If a migration chain supports several historical app versions, test each realistic starting point explicitly.

Common Pitfalls

Skipping intermediate models and trying to force one huge custom migration often makes the logic harder to debug than it needs to be.

Assuming inferred mappings will keep working after major structural changes is another common mistake.

Not testing the intermediate pass outputs can hide bugs until a later pass fails in confusing ways.

Finally, remember that migration code is part of your data contract. Treat it with the same rigor as schema code and production data transformations.

Summary

  • multi-pass Core Data migration means migrating through intermediate model versions rather than in one jump
  • it is useful when one big migration would be too complex or fragile
  • each pass has its own source model, destination model, and mapping model
  • you can mix inferred and custom mappings across passes
  • test every supported starting version and every intermediate migration result, not just the final store

Course illustration
Course illustration

All Rights Reserved.