はじめに
エンジニア歴約3か月のまだまだ勉強中の身ですが、
同じSwift初心者向けにCore Dataで悩んだことを投稿します。
Qiita初投稿ですが、少しでもお力になれれば幸いです。
※この記事ではMacOS Mojave 10.14, Xcode 10.0, Swift4.2を使っています。
Core Dataとは?
ユーザーデータを永続的に保存するときに、UserDefaultsを利用している方も多いかと思いますが、
保存する項目が増えたり、複雑な処理を行う際には、SQLiteを用いてアプリ内のデータベースにデータを保存する方が便利です。
Core Dataはそれをシンプルなコードで実現するフレームワークです。
3rdParty製ライブラリであるRealmも有名ではありますが、
Apple純正のフレームワークということで利用しているiOSアプリも多くあると思います。
ここではCore Dataの具体的な使い方を説明はしませんが、
詳しい使い方等は他のQiita記事を参考にしてください。
(参考)【Swift3】ToDoアプリを作る【CoreData】
マイグレーションについて
一度データベースのmodelを作成した後、新しい機能追加などで属性を追加・削除したり、
エンティティをrenameしたりすることもあると思います。
ただし、一度作成したmodelをそのまま編集し、アプリを起動させるとCoreDataMigrationTest
にてアプリが落ちてしまいます。
単純に起動させるだけであれば、アプリを一度アンインストールした後に再インストールを行うとアプリは起動しますが、既にapp storeにて配布している場合はユーザーに再インストールをお願いすることになるため、現実的ではありません。
そこでdatamodelに変更がある場合でも、アプリが落ちないようにするために、Core Dataのマイグレーションが必要になります。
本記事では、LightWeightMigrationの方法を説明します。
(他にもManual Migrationがあるそうです。)
一度、手を動かせばすぐに慣れるシンプルな方法でした!
公式ドキュメント
Appleに公式ドキュメントはこちらです。
[Using Lightweight Migration]
(https://developer.apple.com/documentation/coredata/using_lightweight_migration)
当然、英語のみのドキュメントで、面食らうこともありますが、
さらっと、
"set the renaming identifier to the name of the property in the source model"
と記載しているものでも、初心者にはなんのこっちゃ分からず、
さらには、
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
do {
try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
} catch {
fatalError("Failed to add persistent store: \(error)")
}
と記載してあるものの、Swift4でデフォルトで挿入される AppDelegate.swift
にそのまま利用することができなかったため、
全体的にハードルが高く、苦労しました。。。
以下、改めてできるだけわかりやすく説明します。
全体像(やること)
- やりたい変更がLightWeightMigrationが該当するかチェック
- 新しいDataModelの作成
- 利用するDataModelを作成した新しいModelに設定
- コードにてLightWeightMigrationを設定
1. やりたい変更がLightWeightMigrationが該当するかチェック
Apple公式ドキュメントによると、LightWeightMigrationが利用できる例は、以下の通りです。
- Attribute(属性)の追加
- Attribute(属性)の削除
- Attribute(属性)のオプショナルへの変更
- Attribute(属性)の非オプショナルへの変更(デフォルト値の設定あり)
- Entity(エンティティ)やAttribute(属性)の名称変更
他にも、試したところによるとType(型)変更も対応できるようですね。
上記に該当しなかったとしても、
簡易的な変更であればLightWeightMigrationが機能する可能性があるので、
一度やってみるのが良いかもしれません。
(そんなに時間はかかりませんので!)
2. 新しいDataModelの作成
Xcodeの左ペインで.xcdatamodeldファイルを選択し、
Xcodeの画面上部ツールバーから、
[Editor]
-> [Add Model Version...]
を選択してください。
適宜、新しいdatamodelファイルの名称を設定します。
(例: CoreDataTest_ver2.xcdatamodel)
Finishすると、作成した.xcdatamodelファイルが追加されています。
新規作成したdatamodelには以前のdatamodelのEntityがコピーされているので、
そこから必要な修正を加えてください。
画像例では、新しいAttribute "address" を追加しました。
3. 利用するDataModelを作成した新しいModelに設定
Xcodeの左ペインで新しく作成したdatamodelを選択し、
Xcode右ペインでFile Inspector
を選択してください。
(documentのようなアイコンの場所です)
[Core Data Model]
-> [Model Version]
-> [Current]
の値を、
先ほど修正を加えた新しいdatamodelへ変更してください。
変更すると、Xcodeの左ペインでCurrentに設定したdatamodelに緑色のチェックマークが追加されます。
4. コードにてLightWeightMigrationを設定
新規Project立ち上げ時に、Use Core Data
にチェックを入れてProjectを作成した場合、
AppDelegate.swift
に以下のように、CoreDataを利用するためのプロパティが記載されています。
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "coreDataTest")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
ここのデフォルトで記載されてされているコードと、Apple公式ドキュメントとの関係性に悩まされたのですが、
Apple公式ドキュメントで記されている内容を紐解くと、要は以下の2つの設定が必要ということでした。
- Migrationを自動で実行するOptionを"true"にする
- MappingModelを自動で実行するOptionを"true"にする
これらの設定をどこで行うかというと、NSPersistentContainer
のpersistentStoreDescriptions
プロパティにありました。
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = true
名前も変わっていて、Apple公式ドキュメントからは辿りつけないですよね。。。
まとめると、デフォルトに記載されているコードに上記を追加すればOKです。
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "coreDataTest")
// Using Lightweight Migration
let description = NSPersistentStoreDescription()
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = true
container.persistentStoreDescriptions = [description]
container.loadPersistentStores(completionHandler: { _, error in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
あとはこちらでbuildして、何ごともなく起動できればOKです!
もちろん、app storeリリース前にはTestFlightを活用して、問題なくmigrationが実行できているか確認をしましょう!
以上、お付き合いいただきありがとうございました。