core-data-patterns
macOSアプリのCore Data永続化パターンです。スタックのセットアップ、CRUD操作、リレーションシップ、マイグレーションに対応しており、データの保存と管理を効率的に実装できます。
description の原文を見る
Core Data persistence patterns for macOS apps. Stack setup, CRUD operations, relationships, migrations.
SKILL.md 本文
Core Data スキル
概要
macOS アプリケーション向けの Core Data 永続化パターン。
スタックセットアップ
actor PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "AppModel")
if inMemory {
container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores { _, error in
if let error {
fatalError("Core Data failed: \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}
var viewContext: NSManagedObjectContext {
container.viewContext
}
func newBackgroundContext() -> NSManagedObjectContext {
container.newBackgroundContext()
}
}
エンティティパターン
@objc(Item)
public class Item: NSManagedObject {
@NSManaged public var id: UUID
@NSManaged public var title: String
@NSManaged public var createdAt: Date
@NSManaged public var updatedAt: Date
}
extension Item {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Item> {
NSFetchRequest<Item>(entityName: "Item")
}
static func create(in context: NSManagedObjectContext, title: String) -> Item {
let item = Item(context: context)
item.id = UUID()
item.title = title
item.createdAt = Date()
item.updatedAt = Date()
return item
}
}
フェッチリクエスト
// 基本的なフェッチ
let request = Item.fetchRequest()
request.predicate = NSPredicate(format: "title CONTAINS[cd] %@", searchText)
request.sortDescriptors = [NSSortDescriptor(keyPath: \Item.createdAt, ascending: false)]
request.fetchLimit = 50
request.fetchBatchSize = 20
let items = try context.fetch(request)
SwiftUI 統合
struct ItemListView: View {
@FetchRequest(
sortDescriptors: [SortDescriptor(\.createdAt, order: .reverse)],
animation: .default
)
private var items: FetchedResults<Item>
var body: some View {
List(items) { item in
Text(item.title)
}
}
}
バックグラウンド操作
func importData(_ data: [ImportItem]) async throws {
let context = PersistenceController.shared.newBackgroundContext()
try await context.perform {
for item in data {
let entity = Item(context: context)
entity.id = UUID()
entity.title = item.title
}
try context.save()
}
}
マイグレーション
- Xcode で新しいモデルバージョンを作成する
- 現在のバージョンとして設定する
- 自動マイグレーションを有効にする:
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = true
テスト
final class CoreDataTests: XCTestCase {
var controller: PersistenceController!
override func setUp() {
controller = PersistenceController(inMemory: true)
}
}
CloudKit コンテナ (DA-4)
iCloud 同期の場合、NSPersistentContainer の代わりに NSPersistentCloudKitContainer を使用してください:
let container = NSPersistentCloudKitContainer(name: "AppModel")
// セットアップ後、CloudKit 同期は自動的に実行されます
// 競合解決は NSMergeByPropertyObjectTrumpMergePolicy を使用します(ローカルが優先)
CloudKit スキーママイグレーション: CloudKit スキーマは加算専用です。フィールドやエンティティを追加することはできますが、削除や名前変更はできません。スキーマは慎重に計画してください。
導出属性 (DA-5)
導出属性を使用して、正規化されていない数値や集計を表現し、負荷の高いフェッチリクエストを回避します:
Xcode モデルエディタで:属性を選択 > Data Model Inspector > Derived > 導出式を設定します。
// 子要素の数:「children.@count」
// 最新日付:「children.@max.createdAt」
導出属性は、保存時に Core Data によって自動的に計算されます。これにより N+1 クエリの問題を回避できます。
抽象エンティティパターン (DA-6)
複数のエンティティ型で共有する属性に抽象エンティティを使用します:
AbstractBaseEntity (抽象)
├── id: UUID
├── createdAt: Date
├── updatedAt: Date
│
├── TaskEntity (具体)
│ └── title: String
│
└── NoteEntity (具体)
└── body: String
使用する場合: 複数のエンティティが 3 個以上の同一属性を共有している。避けるべき場合: id、createdAt、updatedAt のみが共有されている場合(各エンティティに直接追加してください。3 つのフィールドのみのために継承の複雑性は価値がありません)。
Core Data デバッグ (DA-7)
診断用の起動引数:
| 引数 | 表示内容 |
|---|---|
-com.apple.CoreData.SQLDebug 1 | 実行される SQL クエリ |
-com.apple.CoreData.SQLDebug 3 | SQL とバインド変数 |
-com.apple.CoreData.MigrationDebug 1 | マイグレーションステップ |
-com.apple.CoreData.ConcurrencyDebug 1 | スレッド違反 |
-com.apple.CoreData.CloudKitDebug 1 | CloudKit 同期アクティビティ |
Xcode で設定:Edit Scheme > Run > Arguments > Arguments Passed On Launch。
Instruments Core Data テンプレート: フェッチ数、フォルト数、保存期間を表示します。パフォーマンスをデバッグする際に使用します。フォルト数が多い = プリフェッチされなかったオブジェクトがアクセスされている。
データ整合性制約 (DA-8)
ユニーク制約
Xcode モデルエディタで設定:エンティティを選択 > Data Model Inspector > Constraints。制約されたフィールドに重複したエントリを防ぎます。
// エンティティ:Tag
// ユニーク制約:name
// → 同じ名前の 2 つのタグは、重複を作成する代わりにマージされます
検証ルール
モデルエディタで属性ごとに追加:最小値、最大値、文字列の正規表現。
// プログラムの検証(複雑なルール用)
override func validateForInsert() throws {
try super.validateForInsert()
guard title.count >= 1 else {
throw ValidationError.titleRequired
}
}
フェッチリクエスト検証
常に開発時にモデルに対してプレディケートを検証します:
// 文字列ベースのプレディケートの代わりに、型付きキーパスを使用してください
request.predicate = NSPredicate(format: "%K == %@", #keyPath(Item.status), "active")
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- productengineered
- ライセンス
- MIT
- 最終更新
- 2026/4/20
Source: https://github.com/productengineered/parsely / ライセンス: MIT