Core Data vs SwiftData 比較

20年の成熟を誇る、Appleの実戦で鍛えられた永続化フレームワーク

VS
SwiftData

WWDC 2023発表: Swiftネイティブ、@Modelマクロによるゼロボイラープレートの永続化

8 分で読了iOS

スコア比較

グラフを読み込み中...

詳細スコア

詳細スコア: Core Data SwiftData — カテゴリー別10点満点のスコア
カテゴリーCore DataSwiftData
パフォーマンス
9/10
9/10
学習のしやすさ
4/10
9/10
エコシステム
9/10
6/10
コミュニティ
9/10
5/10
求人市場
8/10
6/10
将来性
6/10
10/10

長所と短所

Core Data

長所

  • iOS 3の頃から存在 — 極めて安定しており、ドキュメントも充実
  • NSFetchedResultsControllerによるUITableView/UICollectionViewとの統合が優秀
  • iCloud同期(NSPersistentCloudKitContainer)によるクラウド同期
  • 複雑なリレーションシップとマイグレーションシナリオへの成熟したサポート
  • faulting、バッチ操作といったパフォーマンスチューニング向けの高度な機能
  • Core Spotlight統合によりコンテンツを検索可能に
  • SQLite、Binary、In-Memoryといったストアタイプ
  • 豊富なコミュニティの知見とStack Overflowのリソース

短所

  • NSManagedObjectによる冗長なコード — ボイラープレートが多い
  • スレッドセーフの確保が難しい — NSManagedObjectContextの適切な管理が複雑
  • 学習曲線が急 — NSFetchRequest、NSPredicateの構文
  • エラーメッセージが開発者にとってあまり役に立たない
  • モダンなSwiftで扱うにはラッパーが必要

最適な用途

iOS 16以下のサポートが必要なアプリ複雑なデータリレーションシップとマイグレーション要件iCloud同期が重要なアプリ大規模なデータセットと高度なフェッチ最適化既存のCore Dataプロジェクトの保守

SwiftData

長所

  • @Modelマクロによる1行でのデータモデル定義 — ボイラープレートゼロ
  • SwiftUIとのネイティブ統合 — @Queryによる自動UI更新
  • Swift Concurrencyとの互換 — async/awaitで自然に動作
  • Core Dataの上で動作 — 同じSQLite基盤、同じパフォーマンス
  • iCloud同期の設定がはるかにシンプル
  • 型安全なpredicateとsort descriptor
  • 最小限のマイグレーションAPI — シンプルなスキーマ変更は自動化

短所

  • iOS 17+が必須 — 広範な採用にはまだ早い
  • 複雑なマイグレーションシナリオではCore Dataほどの制御ができない
  • まだ成熟途上 — WWDCごとに大きな変更が入る可能性がある
  • Stack Overflow/コミュニティのリソースがCore Dataと比べてかなり少ない
  • 一部のCore Dataの高度な機能にはまだ相当するものがない

最適な用途

iOS 17+をターゲットにする新規プロジェクトSwiftUIアプリとの永続化統合シンプル〜中程度の複雑さのデータモデル迅速なプロトタイピングと小規模アプリモダンなSwiftチームの新規プロジェクト

コード比較

Core Data
// Core Data - TodoリストのCRUD
import CoreData
import SwiftUI

// NSManagedObjectのサブクラス
@objc(TodoItem)
class TodoItem: NSManagedObject {
    @NSManaged var id: UUID
    @NSManaged var title: String
    @NSManaged var isCompleted: Bool
    @NSManaged var createdAt: Date
}

// ビューモデル
class TodoViewModel: ObservableObject {
    let container: NSPersistentContainer
    @Published var todos: [TodoItem] = []

    init() {
        container = NSPersistentContainer(name: "TodoModel")
        container.loadPersistentStores { _, error in
            if let error { fatalError("Core Data load failed: \(error)") }
        }
        fetchTodos()
    }

    func fetchTodos() {
        let request = NSFetchRequest<TodoItem>(entityName: "TodoItem")
        request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]
        do {
            todos = try container.viewContext.fetch(request)
        } catch {
            print("Fetch error: \(error)")
        }
    }

    func addTodo(title: String) {
        let todo = TodoItem(context: container.viewContext)
        todo.id = UUID()
        todo.title = title
        todo.isCompleted = false
        todo.createdAt = Date()
        saveContext()
    }

    func toggleTodo(_ todo: TodoItem) {
        todo.isCompleted.toggle()
        saveContext()
    }

    private func saveContext() {
        try? container.viewContext.save()
        fetchTodos()
    }
}
SwiftData
// SwiftData - TodoリストのCRUD
import SwiftData
import SwiftUI

// @Modelマクロ — ボイラープレートなし!
@Model
class TodoItem {
    var id: UUID
    var title: String
    var isCompleted: Bool
    var createdAt: Date

    init(title: String) {
        self.id = UUID()
        self.title = title
        self.isCompleted = false
        self.createdAt = Date()
    }
}

// SwiftUI View — @Queryによる自動更新
struct TodoListView: View {
    @Environment(\.modelContext) private var context
    @Query(sort: \.createdAt, order: .reverse) private var todos: [TodoItem]
    @State private var newTitle = ""

    var body: some View {
        List {
            ForEach(todos) { todo in
                HStack {
                    Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle")
                        .foregroundStyle(todo.isCompleted ? .green : .gray)
                        .onTapGesture { todo.isCompleted.toggle() }
                    Text(todo.title)
                        .strikethrough(todo.isCompleted)
                }
            }
            .onDelete { indexSet in
                indexSet.forEach { context.delete(todos[$0]) }
            }
        }
        .toolbar {
            TextField("新しいTodo...", text: $newTitle)
            Button("追加") {
                guard !newTitle.isEmpty else { return }
                context.insert(TodoItem(title: newTitle))
                newTitle = ""
            }
        }
    }
}

結論

iOS 17+をターゲットにするならSwiftDataから始めましょう — コード量が少なく、エラーが起きにくく、SwiftUIとの統合も優れています。古いデバイスのサポート、複雑なマイグレーション、あるいは大規模なプロダクションのCore Data基盤がある場合はCore Dataを選びましょう。両者は同じ基盤を共有しているため、理論上は併用も可能です。

無料相談を受ける
FAQ

よくある質問

長期的にはそうなるでしょう、Appleの投資の方向性がそちらを向いています。しかしCore Dataが廃止されるまでには何年もかかります — 既存のCore Dataプロジェクトは引き続きサポートされます。

関連ブログ記事

すべての記事を見る
すべての比較