【初心者向け】Core DataのLightWeightMigrationの方法【Swift4】

## はじめに

エンジニア歴約３か月のまだまだ勉強中の身ですが、
同じ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】](https://qiita.com/snowman_mh/items/0d27f4abf7b14c72107f)

## マイグレーションについて

一度データベースの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)

当然、英語のみのドキュメントで、面食らうこともありますが、
さらっと、

<font color="DeepPink">"set the renaming identifier to the name of the property in the source model"</font>

と記載しているものでも、初心者にはなんのこっちゃ分からず、

さらには、

```swift

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`にそのまま利用することができなかったため、
全体的にハードルが高く、苦労しました。。。

以下、改めてできるだけわかりやすく説明します。

## 全体像（やること）

1. やりたい変更がLightWeightMigrationが該当するかチェック
2. 新しいDataModelの作成
3. 利用するDataModelを作成した新しいModelに設定
4. コードにてLightWeightMigrationを設定

## 1. やりたい変更がLightWeightMigrationが該当するかチェック

Apple公式ドキュメントによると、LightWeightMigrationが利用できる例は、以下の通りです。
<font color="DeepPink">
- Attribute(属性)の追加
- Attribute(属性)の削除
- Attribute(属性)のオプショナルへの変更
- Attribute(属性)の非オプショナルへの変更（デフォルト値の設定あり）
- Entity（エンティティ）やAttribute(属性)の名称変更
</font>

他にも、試したところによるとType（型）変更も対応できるようですね。
上記に該当しなかったとしても、
簡易的な変更であればLightWeightMigrationが機能する可能性があるので、
一度やってみるのが良いかもしれません。
（そんなに時間はかかりませんので！）

<img width="461" alt="スクリーンショット 2018-12-13 14.37.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/137510/9566541c-7a20-b0a0-7f95-41e393129a1f.png">

## 2. 新しいDataModelの作成

Xcodeの左ペインで.xcdatamodeldファイルを選択し、
Xcodeの画面上部ツールバーから、
`[Editor]` -> `[Add Model Version...]`　を選択してください。

<img width="700" alt="スクリーンショット 2018-12-13 14.37.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/137510/98db120a-e12e-84ef-4864-74499252ad23.png">

適宜、新しいdatamodelファイルの名称を設定します。
(例：　CoreDataTest_ver2.xcdatamodel)

<img width="727" alt="スクリーンショット 2018-12-13 14.37.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/137510/ed9b3675-f4f4-a208-f0f1-f0e2ffb7c974.png">

Finishすると、作成した.xcdatamodelファイルが追加されています。

<img width="379" alt="スクリーンショット 2018-12-13 14.38.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/137510/13cf753c-254f-41b5-3a9f-c51a3e7e9820.png">

新規作成したdatamodelには以前のdatamodelのEntityがコピーされているので、
そこから必要な修正を加えてください。

画像例では、新しいAttribute "address" を追加しました。

<img width="523" alt="スクリーンショット 2018-12-13 14.39.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/137510/bc462e5a-f6e8-c471-883f-fc5b2d8c6b32.png">

## 3. 利用するDataModelを作成した新しいModelに設定

Xcodeの左ペインで新しく作成したdatamodelを選択し、
Xcode右ペインで`File Inspector`を選択してください。
（documentのようなアイコンの場所です）

`[Core Data Model]` -> `[Model Version]` -> `[Current]` の値を、
先ほど修正を加えた新しいdatamodelへ変更してください。

<img width="293" alt="スクリーンショット 2018-12-13 14.41.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/137510/86edfde4-24f6-1a1a-305b-c7e319f339e8.png">

変更すると、Xcodeの左ペインでCurrentに設定したdatamodelに緑色のチェックマークが追加されます。

<img width="375" alt="スクリーンショット 2018-12-13 14.42.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/137510/d2fce078-424b-5b24-708c-65d309ecbd61.png">

## 4. コードにてLightWeightMigrationを設定

新規Project立ち上げ時に、`Use Core Data`にチェックを入れてProjectを作成した場合、
`AppDelegate.swift`に以下のように、CoreDataを利用するためのプロパティが記載されています。

```swift
    // 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公式ドキュメントで記されている内容を紐解くと、要は以下の２つの設定が必要ということでした。

- Migrationを自動で実行するOptionを"true"にする
- MappingModelを自動で実行するOptionを"true"にする

これらの設定をどこで行うかというと、`NSPersistentContainer`の`persistentStoreDescriptions`プロパティにありました。

```swift
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = true
```

名前も変わっていて、Apple公式ドキュメントからは辿りつけないですよね。。。

まとめると、デフォルトに記載されているコードに上記を追加すればOKです。

```swift
    // 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が実行できているか確認をしましょう！

以上、お付き合いいただきありがとうございました。