Core Dataとは?NSManagedObjectの仕組みとSwiftDataとの使い分け
Core DataはAppleが2005年のmacOS 10.4で導入した永続化フレームワークで、iOSでは3.0から使えます。SQLiteのテーブルを直接書き換える代わりに、データをNSManagedObjectのグラフとして扱い、変更の追跡・保存・取り消しをフレームワーク側が引き受ける設計です。この記事では中核4クラスの分担、NSPersistentContainerを起点にした最小実装、モデル変更時の軽量移行と段階移行、SwiftDataやSQLite直接構成との違いを実装者の視点で整理します。
まとめ:Core Dataの採用判断とSwiftDataとの線引き
対象OSをiOS 17以降に引けて、端末内の保存が単純なCRUD中心の新規アプリなら、選ぶべきはSwiftDataです。Core Dataを新しく選ぶ理由は後述の3条件に絞られ、どれにも当たらないなら新規採用の動機は薄いままです。
逆に、既存アプリをSwiftDataへ全面移植する必要もありません。両者は同一の永続ストアを共有でき、新機能だけをSwiftDataで書く共存構成が取れる。判断軸は「複雑な述語・集計・フェッチ制御を抱えているか」の一点で、抱えているなら据え置き、単純な読み書きが中心なら段階的に移す価値が出ます。
実装で事故が起きるのは、保存処理そのものではなく並行処理とモデル移行の2箇所です。この2点を先に設計しておけば、学習コストの大半は回収できます。
Core Dataの定義とオブジェクトグラフ管理が担う4つの役割
Core Dataを「iOS版のデータベース」と説明すると実態を外します。データベースエンジンではなく、オブジェクトのグラフを管理して永続ストアへ写す層だからです。既定の保存先はSQLiteですが、binary、XML(macOSのみ)、in-memoryも選べます。
NSPersistentContainerが束ねる4クラスの関係
構成要素は4つです。スキーマを表すNSManagedObjectModel、永続ストアとの接続を持つNSPersistentStoreCoordinator、変更を溜めるNSManagedObjectContext、1件のレコードに対応するNSManagedObject。iOS 10で追加されたNSPersistentContainerは前3者の生成と結線をまとめたもので、iOS 9以前の50行ほどの定型コードが不要になりました。
オブジェクトとテーブルの対応付けという意味で、Core DataはORM(Object-Relational Mapping)と役割が重なります。ただしリレーショナルモデルへの写像は保証されません。SQLの発行方法もテーブル構造も内部実装で、生成SQLに依存した設計は公式に想定されていない点は押さえておきます。
NSManagedObjectContextが担う変更管理と保存の境界
コンテキストは「まだ保存していない編集内容を溜めておく箱」です。NSManagedObjectのプロパティを書き換えても、その時点ではメモリ上の変更にとどまります。saveを呼んで初めてストアへ書き込まれる。この二段構えのおかげで、入力途中のrollbackやundoManagerによる取り消しが標準で成立します。
コンテナが持つviewContextはメインキューに紐づき、UI表示専用に使います。書き込みを伴う処理は別コンテキストへ回す。この線引きを曖昧にした実装が、後述するスレッド跨ぎのクラッシュを生みます。
xcdatamodeldで定義するエンティティ・属性・関連の設計
スキーマはXcodeのモデルエディタでxcdatamodeldとして定義します。エンティティが表、属性が列、リレーションシップが関連に相当し、関連には逆方向(inverse)の指定が要る。空のまま放置すると削除時の整合が崩れます。
削除規則(Delete Rule)はNullify、Cascade、Deny、No Actionの4種類。既定のNullifyは関連を切るだけで子オブジェクトを残すため、親を消したら子も消す設計ならCascadeを明示します。孤児レコードが積み上がる不具合は、運用開始から半年ほど経ちストア肥大として表面化しがちです。
NSPersistentContainerの初期化からCRUDまでの実装手順
最小構成なら、起動時にコンテナを1つ作って保持し、コンテキスト越しに読み書きするだけで動きます。手順の順序と、どの処理をどのコンテキストで走らせるかを外さなければ、詰まる箇所は多くありません。
コンテナ生成とloadPersistentStoresの記述順序
起動処理は次の順で組み立てます。
NSPersistentContainer(name:)にモデル名を渡して生成する- 必要なら
persistentStoreDescriptionsで履歴追跡やストア種別を設定する loadPersistentStoresを呼び、クロージャでエラーを受け取るviewContext.automaticallyMergesChangesFromParentを有効にする
手順2を手順3の後に書いても、ストアを開いた後の記述は無視されるため設定は反映されません。手順4を省くと、バックグラウンドで保存した内容が画面へ反映されず、「保存されない」という誤診に時間を取られます。
NSFetchRequestとNSPredicateで組み立てる取得処理
取得はNSFetchRequestに条件と並び順を載せてfetchを呼ぶ形です。条件はNSPredicate、並び順はNSSortDescriptorの配列で渡す。文字列連結で述語を組み立てず、NSPredicate(format:)の引数置換を使うのが基本です。
件数だけ知りたいのに全件取得して数えるのは無駄が大きい。context.count(for:)ならオブジェクトを生成せずに件数を返します。一覧が数百件を超えるならfetchBatchSizeを20前後に設定する。この2つで1万件規模のスクロールは体感が変わります。
saveの呼び出し位置とperformBackgroundTaskの使い分け
重い書き込みはcontainer.performBackgroundTaskへ渡します。プライベートキューのコンテキストが作られるため、メインスレッドを止めずに済む。CSV取り込みのような一括登録なら、iOS 13で追加されたNSBatchInsertRequestがさらに速く、メモリも膨らみません。
1件ごとにsaveを呼ぶ実装は避けます。トランザクションが件数分走るためです。500〜1,000件ごとに区切ってまとめて保存し、失敗時はrollbackで戻す。区切りごとにresetを呼べばピークメモリも抑えられます。
一覧画面の更新を担う結果コントローラと@FetchRequest
NSFetchedResultsControllerは、フェッチ結果とテーブル表示を結びつけて差分更新を流すクラスです。レコードが追加・変更・削除されると行単位の変更をデリゲートへ通知するため、UIKitの一覧では実装量が変わります。SwiftUIでは@FetchRequestが同じ役割を担う。混在プロジェクトでは画面ごとにどちらを使うか先に決めないと、更新経路が二重になります。
SQLite・Realm・SwiftDataと比べたCore Dataの守備範囲
永続化の選択肢は4つあり、分かれるのは変更追跡とUI更新を任せられる範囲、対応OSの下限、クラウド同期の到達点の3点です。
SQLiteを直接扱う構成と比べたCore Dataの利得と制約
SQLiteをGRDBなどのラッパ経由で直接扱う構成は、実行計画のチューニングから複雑なJOIN、ウィンドウ関数まで手が届きます。代わりに変更追跡・undo・UI差分更新・iCloud同期はすべて自作です。
| 観点 | SQLite直接 | Core Data | SwiftData |
|---|---|---|---|
| モデル定義 | SQL文とマッピング自作 | xcdatamodeldで定義 | @Model付きクラス |
| 変更追跡 | 自前で実装 | コンテキストが追跡 | コンテキストが追跡 |
| 対応OSの下限 | 制約なし | iOS 3.0以降 | iOS 17.0以降 |
| 一覧のUI更新 | 自前で通知設計 | 結果コントローラで差分 | @Queryで自動更新 |
| iCloud同期 | 自前実装 | CloudKit連携APIあり | 設定で有効化 |
| 共有DB同期 | 自前実装 | CloudKit Sharing対応 | 2026年8月時点で非対応 |
集計クエリが中心のアプリでは、生成SQLに介入できない抽象化が足かせになります。画面と1対1で対応するオブジェクトを編集して保存する業務アプリなら、逆に自作すべき層が丸ごと消える利得が勝ちます。
SwiftDataとの実装作法の違いと同一ストアで共存する条件
SwiftDataはCore Dataの実装を土台にした上位レイヤで、xcdatamodeldもサブクラス生成も要りません。Swiftのクラスに@Modelを付ければスキーマが生成される。両者の詳しい対応関係はSwiftDataとは?@Modelの仕組みとCore Dataとの違いで整理しました。
同一ストアでの共存には条件が3つあります。Core Data側で永続履歴追跡を有効にすること、エンティティのクラス名がSwiftData側と衝突しないこと、両者が同じストアURLを指すこと。履歴追跡はSwiftData側が既定で有効なので、Core Data側を揃えないとストアが読み取り専用になり、書き込みが黙って失敗します。
Realmを選ぶ案件とCore Dataが有利になる案件の境界
サードパーティ製のRealm(モバイルDB)は、AndroidとiOSでスキーマ定義を揃えられる点が最大の差です。クロスプラットフォームでデータ層を共通化したい案件では検討に値します。
一方、Apple純正であることが効く場面もはっきりしています。iCloud同期をOSの仕組みに乗せたい、CloudKitの共有機能を使いたい、外部依存を増やせない。外部SDKは提供方針の変更が起きうるため、10年単位で保守する業務アプリでは依存の少なさ自体が要件になります。
NSPersistentCloudKitContainerによるiCloud同期の範囲
iOS 13で追加されたNSPersistentCloudKitContainerは、コンテナのクラスを差し替えるだけでストアをCloudKitへ鏡写しにします。競合解決や差分送信はフレームワーク側の担当です。ただしモデル側に条件があり、全属性にデフォルト値かオプショナル指定が要る、一意制約は使えない、リレーションシップは双方向必須、の3点を満たす設計が前提になります。
2026年8月時点で、CloudKitの共有データベース(他ユーザーとレコードを共有する仕組み)に対応するのはNSPersistentCloudKitContainerとCloudKit Sharingの組み合わせだけです。SwiftDataは私有データベースの同期までにとどまります。
モデル変更で走る軽量移行と段階移行(staged migration)の使い分け
リリース済みアプリでスキーマを変えると、端末に残った旧ストアを新モデルへ合わせる処理が要ります。設計しないままバージョンを重ねると、ある更新を境にアプリが起動しなくなる。最も再現しにくい不具合が集まる領域です。
軽量移行が自動で通る条件とマッピングモデルが必要な変更の線引き
属性の追加、削除、リネーム(renaming identifierの指定つき)、オプショナル化、エンティティの追加は軽量移行の対象です。ストア記述でshouldMigrateStoreAutomaticallyとshouldInferMappingModelAutomaticallyを有効にしておけば、ストアを開く際に自動で走ります。
条件を外れるのは値の変換を伴う変更です。1つの属性を2つに分割する、格納形式を変える、関連の張り替えで再計算が要る。この場合はマッピングモデルか段階移行でコードを書きます。判断の目安は「旧データの値を読んで加工する必要があるか」の一点です。
NSStagedMigrationManagerで多段の移行を分解する手順
WWDC23で公開された段階移行は、iOS 17以降で使えます。NSStagedMigrationManagerにNSLightweightMigrationStageとNSCustomMigrationStageを順に並べて渡す形です。v1からv4へ一気に変換せず、v1からv2は軽量、v2からv3はカスタム、と段ごとに分ける。
カスタム段ではwillMigrateHandlerとdidMigrateHandlerにコードを書き、移行前後のコンテキストからデータを読んで書き換えます。マッピングモデル方式に対する実利は、変換処理がSwiftコードとしてレビューでき、テストを書ける点です。
リリース後に移行が失敗してストアが開けなくなる原因と検証手順
移行の不具合はテスト環境で再現しにくい。新規インストールでは移行そのものが走らないためです。検証は旧バージョンのストアを用意して行います。
- 旧バージョンをインストールしてデータを作り、そのまま新版へ上書き更新する
- v1からv2、v1からv3、v2からv3のように、飛び級の経路をすべて通す
- 移行後に件数と代表レコードの値を照合する
飛び級の検証を省いた結果、2世代前からの更新でだけ落ちる不具合は珍しくありません。旧ストアファイルはテスト資産としてリポジトリに保管し、モデルの版を増やすたびに検証セットへ1経路ずつ足していきます。
2026年に新規案件でCore Dataを採用する条件と見送る場面
2026年8月時点で、新規のiOSアプリにCore Dataを選ぶ理由は3つしかありません。CloudKit共有データベースが要る、iOS 16以前を対象に含む、既存資産を引き継ぐ。どれにも当たらないならSwiftDataを選び、Core Dataは見送って構いません。
CloudKit共有データベースが要る案件でCore Dataを残す判断
家族で共有する家計簿、チームで編集するタスク管理、店舗スタッフが見る在庫台帳。他ユーザーとレコードを共有する要件が入った時点で、共有データベース同期に未対応のSwiftDataは選べません。
この場合はCore DataとNSPersistentCloudKitContainerで組み、共有用のストアを私有ストアと分けます。要件定義で「共有」の一語が出たら、永続化層はCore Dataで確定させる。後から足す前提でSwiftDataを選ぶと、データ層の作り直しになります。
対象OSにiOS 16以前を含む案件での永続化フレームワーク選定
SwiftDataの対応はiOS 17.0以降です。法人向け業務アプリで古い端末が現場に残っている、配布済みiPadの更新が止まっている、といった案件では対象OSの下限が下がる。iOS 16以前を1台でも含むなら、SwiftDataは候補から外れます。
対象OSの下限は要件定義で先に固めます。着手後に「iOS 15も対象」と判明すると、永続化層の設計をやり直すためです。端末構成が読めない段階では、Core Dataを選ぶほうが手戻りは小さく収まります。
数十万件規模の集計と全文検索でCore Dataを見送る条件
端末内で数十万件を集計する、日本語の全文検索を高速に返す。この2つが機能の中心にあるなら、生成SQLに介入できずインデックス設計の自由度も限られるCore Dataは向きません。
この場合はSQLiteを直接扱う構成へ寄せ、全文検索はFTS5のような専用機構に任せます。「Core Dataで作ってから遅ければ差し替える」という進め方は勧めません。永続化層の差し替えはデータ移行を伴うためです。
既存Core Dataアプリの保守と外注で詰まる失敗パターン
Core Dataアプリを引き継ぐとき、コードの読解より先に効くのは並行処理の作法とモデル資産の管理状況です。この2つが崩れていると、見積もりの前提から変わります。
concurrencyDebug=1で洗い出すスレッド跨ぎのクラッシュ
NSManagedObjectContextとNSManagedObjectは、生成されたキュー以外から触れません。バックグラウンドで取得したオブジェクトをメインスレッドへ渡す一行が、原因不明のクラッシュを生みます。しかも即座には落ちず、数十分後に現れることがある。
切り分けにはスキームの起動引数へ-com.apple.CoreData.ConcurrencyDebug 1を追加します。違反した時点で停止するため原因箇所が特定できる。キューを跨ぐときはオブジェクトではなくobjectIDを渡し、受け側のコンテキストで再取得します。この作法だけでこの種の不具合はほぼ消えます。
xcdatamodeldの版数管理を引き継げないまま起きる事故
引き継ぎで最も困るのは、過去のモデルバージョンがリポジトリから消えているケースです。xcdatamodeldはバージョンごとのサブディレクトリを内包する形式で、旧バージョンを削除するとその版からの移行経路が永久に失われます。旧版を使い続けているユーザーの端末は、更新した瞬間にデータを失う。
引き継ぎ時に確認するのは、過去バージョンがすべて残っているか、現在のモデルにどの版が設定されているか、旧ストアからの移行テストが存在するかの3点です。ひとつでも欠けていれば、旧バージョンの復元と移行検証から着手します。この工数を見積もりへ入れずに受けると、初回リリースで事故が起きます。
永続化層を含むiOSアプリ開発を外注するときの体制と確認基準
永続化層の設計は、画面の実装と違って完成後の手直しが効きません。外注先を選ぶ際は、成果物の見た目ではなく次の3点を確認します。コンテキストの分離方針が文書化されているか、モデルバージョンの追加手順が決まっているか、移行の検証手順が引き継ぎ資料に含まれるか。
一創ではiOSアプリ開発として、Core Dataを含む永続化層の設計から保守・SwiftDataへの段階移行までを受託しています。発注側で全体像を押さえたい場合はiOSアプリ開発とは?特徴・Swiftでの作り方・費用と外注判断も併せて参照してください。引き継ぎ案件では、初回にモデルバージョンと移行テストの棚卸しを実施したうえで見積もりを提示しています。
よくある質問
Core Dataの選定と実装でよく挙がる質問を5つ整理します。
Core DataはSQLiteと何が違うのですか?
層が違います。SQLiteはデータベースエンジンで、Core Dataはオブジェクトのグラフを管理して永続ストアへ写す仕組みです。既定の保存先はSQLiteですが、binaryやin-memoryも選べる。SQLを書いて実行計画を制御したいならSQLiteを直接扱い、変更追跡やUIの差分更新を任せたいならCore Dataを選びます。
Core Dataは2026年時点でも新規採用してよいのですか?
条件つきで問題ありません。2026年8月時点でAppleはCore Dataの廃止を予告しておらず、CloudKitの共有データベース同期に対応する唯一の一次経路でもあります。ただし新規案件の既定はSwiftDataで、共有データベースが要る、iOS 16以前を含む、既存資産を引き継ぐ、のいずれかに当たる場合にCore Dataを選びます。
Core DataとSwiftDataは同じアプリに同居できますか?
同居できます。両者は同一の永続ストアを共有でき、既存画面をCore Dataのまま残して新機能だけSwiftDataで書く構成が取れる。条件は、Core Data側で永続履歴追跡を有効にすること、エンティティのクラス名を衝突させないこと、同じストアURLを指すことの3つです。履歴追跡を揃えないとストアが読み取り専用になります。
Core Dataでバックグラウンド保存をすると落ちるのはなぜですか?
コンテキストとオブジェクトを、生成されたキューの外から触っている可能性が高いところです。バックグラウンドで取得したNSManagedObjectをメインスレッドへ渡すと、後から不定期にクラッシュします。起動引数へ-com.apple.CoreData.ConcurrencyDebug 1を追加すれば違反箇所で停止する。キューを跨ぐ場合はobjectIDだけを渡してください。
Core Dataのモデル変更はどこまで自動で移行できますか?
属性の追加・削除・リネーム、オプショナル化、エンティティの追加までは軽量移行が自動で処理します。ストア記述で自動移行と推論を有効にしておけば、ストアを開く際に走る。値の変換や属性の分割を伴う変更は対象外で、iOS 17以降ならNSStagedMigrationManagerで軽量な段とカスタムな段に分けて記述します。
関連記事
- Combine(Swift)とは?基本概念とフレームワーク概要:変更をUIへ流す通知経路の設計と対比できます。
- SwiftUIとUIKitの違いとは?2026年の使い分けと相互運用:結果コントローラと@FetchRequestの選択の前提です。
- FTS5とは何か?SQLiteに組み込まれた全文検索エンジンの仕組み:全文検索でCore Dataを見送る場合の受け皿です。
- iOS 26.6とは?変更点とセキュリティ修正・業務端末での適用判断:対象OSの下限を決める材料になります。
- アプリ開発の費用相場は?規模・OS・開発手法別の内訳と抑え方:永続化層を含む見積もりの全体感です。