Database Migrations in Swift (iOS/macOS): Overview of Tools and Examples
Database migrations are the process of controlled changes to the data schema (tables, indexes, relationships) without losing existing information. In the Swift ecosystem for Apple platforms (iOS, macOS, watchOS, tvOS), developers use several approaches: from the built-in Core Data to third-party libraries such as GRDB.swift and Fluent. In this article, we will review the key tools, their capabilities, and provide practical code examples.
1. Core Data and Migrations (Lightweight and Manual)
Core Data is Apple's standard framework for working with an object model. Migrations are divided into two types:
- Lightweight Migration — automatic, if the changes do not involve complex transformations (adding attributes, deleting, renaming with a mapping model).
- Manual Migration — required when you need to change data types, merge entities, or perform custom transformations.
Example of setting up lightweight migration in Swift:
import CoreData
lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "MyDataModel") let description = container.persistentStoreDescriptions.first description?.shouldMigrateStoreAutomatically = true description?.shouldInferMappingModelAutomatically = true container.loadPersistentStores { _, error in if let error = error { fatalError("Error loading Core Data: \\(error)") } } return container}()2. GRDB.swift — Modern SQLite Library with Migrations
GRDB.swift is a powerful library for working with SQLite that offers a built-in migration mechanism. It allows you to version the database schema and perform changes sequentially.
Installation via Swift Package Manager:
// Package.swiftdependencies: [ .package(url: "https://github.com/groue/GRDB.swift.git", from: "6.0.0")]Example migration with GRDB.swift:
import GRDB
var migrator = DatabaseMigrator()
// Migration 1: create tablemigrator.registerMigration("v1_create_users") { db in try db.create(table: "users") { t in t.autoIncrementedPrimaryKey("id") t.column("name", .text).notNull() t.column("email", .text).unique() }}
// Migration 2: add columnmigrator.registerMigration("v2_add_age") { db in try db.alter(table: "users") { t in t.add(column: "age", .integer).defaults(to: 0) }}
// Apply migrations at app startuplet dbQueue = try DatabaseQueue(path: "/path/to/database.sqlite")try migrator.migrate(dbQueue)3. Fluent (Vapor) — ORM for Server-Side Swift
Fluent is an ORM that is part of the Vapor framework (server-side Swift for macOS). Migrations in Fluent are described as classes implementing the Migration protocol.
Example migration for Fluent 4:
import Fluent
struct CreateUser: Migration { func prepare(on database: Database) -> EventLoopFuture<Void> { database.schema("users") .id() .field("name", .string, .required) .field("email", .string, .required).unique(on: "email") .create() } func revert(on database: Database) -> EventLoopFuture<Void> { database.schema("users").delete() }}
// Registration in configure.swiftapp.migrations.add(CreateUser())try app.autoMigrate().wait()4. Tool Comparison
- Core Data — suitable for Apple applications tied to an object model. Migrations are often automatic, but complex scenarios require mapping