【SwiftUI】CoreDataを使う

# はじめに
https://qiita.com/SNQ-2001/items/61cef2153aad5c4d542f

以前、「コードのみでCoreDataを実装する」を書きました。
しかし、それ以来CoreDataを使ったことがありませんでした。
先日使うことがあったので使い方を記録しておきます。

# サンプルアプリ
![Simulator Screen Recording - iPhone 14 Pro - 2023-01-29 at 19 14 48](https://user-images.githubusercontent.com/84154073/215320876-026a8dd5-70ff-4bd6-be5d-5394bf5c7650.gif)

# 実装
### xcdatamodeldを作成
① メインターゲットフォルダを右クリックします
② 「New File...」を選択します
<img width="957" alt="スクリーンショット 2023-01-29 15 36 14" src="https://user-images.githubusercontent.com/84154073/215309572-96745a6d-6549-425e-9e39-92da7b86b338.png">

③ `Data`を検索します
④ 「Data Model」を選択します
⑤ 「Next」を選択します
<img width="957" alt="スクリーンショット 2023-01-29 15 48 11" src="https://user-images.githubusercontent.com/84154073/215310052-40f80bde-b686-45e1-82dd-102de30c68ba.png">

⑥ 任意のファイル名を設定します
⑦ 「Create」を選択します
<img width="957" alt="スクリーンショット 2023-01-29 15 50 49" src="https://user-images.githubusercontent.com/84154073/215310141-bcb41823-bda0-448a-bdbe-21d5acfcca72.png">

### Entityの設定
① 先ほど作成したファイルを選択します
② 「Add Entity」を選択します
<img width="957" alt="スクリーンショット 2023-01-29 15 54 48" src="https://user-images.githubusercontent.com/84154073/215310338-313aa8c4-04e1-41fa-a838-c88417f84396.png">

③ 追加したEntityを任意の名前にリネームします
<img width="957" alt="スクリーンショット 2023-01-29 15 56 36" src="https://user-images.githubusercontent.com/84154073/215310401-d9694a79-0cf8-4d72-a5f9-a1482e04cd4d.png">

④ 「+」を選択します
⑤ Attribute名を決めます
⑥ Typeを選択します
<img width="957" alt="スクリーンショット 2023-01-29 15 58 39" src="https://user-images.githubusercontent.com/84154073/215310500-9684d5f9-0951-4af5-82ea-542bd6da3e9c.png">

⑦ 追加したEntityを選択します
⑧ 三本線を選択します
⑨ 「Codegen」を選択します。
<img width="816" alt="スクリーンショット 2023-01-29 18 17 46" src="https://user-images.githubusercontent.com/84154073/215316962-b6153393-f357-44f0-b379-f6e5260993b7.png">

⑩ 「Codegen」を「Manual/None」に変更します
<img width="816" alt="スクリーンショット 2023-01-29 18 20 04" src="https://user-images.githubusercontent.com/84154073/215317013-a91f2c2c-0c89-48c9-9040-8398c6cb21d6.png">

### 拡張を追加
① 「Editor」を選択します
② 「Create NSManagedObject Subclass...」を選択します
<img width="957" alt="スクリーンショット 2023-01-29 16 04 00" src="https://user-images.githubusercontent.com/84154073/215310707-e8da7356-da60-4f51-966b-f78efbc12d4b.png">

③ 「Next」を選択します
<img width="730" alt="スクリーンショット 2023-01-29 16 07 08" src="https://user-images.githubusercontent.com/84154073/215310949-741b8a63-cf78-4c7a-a66d-0bf06fdbcf64.png">

④ 「Next」を選択します
<img width="730" alt="スクリーンショット 2023-01-29 16 08 29" src="https://user-images.githubusercontent.com/84154073/215310997-4c25dd5c-7c70-41f1-9578-be355b20ec48.png">

⑤ フォルダを選択します
⑥ 「Create」を選択します
<img width="720" alt="スクリーンショット 2023-01-29 16 10 05" src="https://user-images.githubusercontent.com/84154073/215311056-bb8272fa-23c5-48a2-8a8c-932286b3f9f9.png">

⑦ ターゲットフォルダ内に移動させます。
<img width="960" alt="スクリーンショット 2023-01-29 16 12 08" src="https://user-images.githubusercontent.com/84154073/215311127-256ea456-243d-4354-a69d-bc6d0f310029.png">

### Persistenceを作成する
① メインターゲットフォルダを右クリックします
② 「New File...」を選択します
<img width="960" alt="スクリーンショット 2023-01-29 18 08 03" src="https://user-images.githubusercontent.com/84154073/215316534-b2650ccc-9001-4719-bc4d-abc98d5e96cd.png">

③ 「Swift File」を選択します
④ 「Next」を選択します
<img width="742" alt="スクリーンショット 2023-01-29 18 11 01" src="https://user-images.githubusercontent.com/84154073/215316616-097a7ac6-d73d-4800-8c05-99b225e70df9.png">

⑤ `Persistence`というファイル名にします(自動生成時のファイル名がこれだった)
⑥ 「Create」を選択します
<img width="812" alt="スクリーンショット 2023-01-29 18 13 16" src="https://user-images.githubusercontent.com/84154073/215316730-55e136cb-7df4-4846-a4ea-db7781a31c17.png">

作成したファイルに以下のコードをコピペします
```swift:Persistence
import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        // MARK: 下2行はEntityの設定によって違う
        let newSampleModel = SampleModel(context: viewContext)
        newSampleModel.title = ""
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        // MARK: 下1行はEntityの設定によって違う
        container = NSPersistentContainer(name: "SampleModel")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { _, error in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}
```

### App.swiftの編集
```swift
import SwiftUI

@main
struct coredata_sampleApp: App {
    private let persistence = PersistenceController.shared
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistence.container.viewContext)
        }
    }
}
```

### サンプルアプリ
アプリを起動した時間をCoreDataに保存するアプリです。
```swift
import SwiftUI

struct ContentView: View {
    // MARK: `SampleModel`は各自の設定によって違います
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \SampleModel.title, ascending: false)],
        animation: .default
    )
    private var samples: FetchedResults<SampleModel>

    // MARK: 追加/削除に必要
    @Environment(\.managedObjectContext) private var context

    var body: some View {
        List(samples) { sample in
            Text(sample.title ?? "")
        }
        .onAppear {
            // MARK: ここが追加の処理
            let newSampleModel = SampleModel(context: context)
            newSampleModel.title = "\(Date.now.timeIntervalSince1970)"

            try? context.save()
        }
    }
}
```

# おわり
無事、CoreDataを使用することができました！