爆速CoreData

## はじめに
こんにちは、watnowのアドベントカレンダー初日を担当させていただきます。
よろしくお願いします！
記事を書くのは2回目なのでかなり拙い文章となっております、ご了承ください。:bow_tone1:

そしてこの記事は、
##### いち早くCoreDataを使いたい！！

そんな人向けの記事になっているので詳しいことは個人的に分かり易いなと思った記事を貼っておくので詳しくはそちらで学習してもらえればと思います。
では本題に移ります。

 ## CoreDataってなに？(爆速)
 CoreDataは、ローカルにデータを保存、取得などをするためのCocoaFrameWorkに搭載されたO\RマッパーのことでありDB自体はSQLiteを使っています。
 
:::note info
 O\Rマッパー
 O\RマッパーとはオブジェクトとRDBをつなぐ橋渡しのようなもの
 通常プログラムでインスタンスの値を保存するSQL文などを生成するコードを書かなければいけないが、O\Rマッパーはあらかじめ設定しておいた対応関係から自動でデータベースの対応するテーブルに書き出しや逆にデータを読み込みインスタンスに代入などをしてくれるというものです。
:::
 つまりXCode上で対応関係を設定すれば直接DBを管理しなくてもデータを保存、取得できるということ！
 
 説明はこれくらいにして早速爆速CoreDataしましょう！
## 爆速CoreData
今回はCoreDataを使って、データの保存、取得、削除をできるメモアプリを作りながら説明していこうと思います。
### セットアップ編
新しいプロジェクトを作成する場合
[File]->[New]->[Project]->Strage欄からCoreDataを選択して-> Next
![スクリーンショット 2023-11-29 18.19.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058061/0251408c-759d-218d-de1c-6a03e90bdd2c.png)
既にプロジェクトがある場合
[File]->[New]->[File...]でCoreDataのところまでスクロール
↓
DataModel -> [Next]
タイトルは各々好きな名前を設定していただいて、Create！


するとこのようなファイルが生成されていると思います。
![スクリーンショット 2023-11-29 18.46.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058061/8b16b97d-428a-04e0-6adb-9e2f6302e190.png)
後からCoreDataを追加した方はPersistence.swiftを追加し,
```swift:Persistence.swift
//
//  Persistence.swift
//  QiitaCoreData
//
//

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        for _ in 0..<10 {



            //　各々のEntity名、Attribute名でカスタムしてください
            /*
             let newEntity名 = Entity名(context: viewContext)
             newEntity名.Attribute名 = 初期値
             */


            let newMemo = Memo(context: viewContext)
            newMemo.memo = ""
        }
        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) {
        container = NSPersistentContainer(name: "QiitaCoreData")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}

```

これをコピペしてください


ここからメモに必要な情報の登録をしていきます。
.xcdatamodelIdを選択し、
ENTITIES名をMemoに変更

:::note warn
初め大文字で書かないと注意されます。
:::
Attribute欄の＋ボタンを押し、ここに必要な情報とその型を設定します。
今回は文字列を保存するのでmemo: String

:::note warn
Atterbute名は小文字から始めないと注意されます。
:::
timestampは今回使わないのでtimestampをクリックし　−　を押して消します。
右のEntityのcodegen欄はManual/Noneを選択します。
爆速なのでこの辺の詳細は、とてもわかりやすい記事があるので気になる方はそちらをみてください。
[SwiftDataの前にCore Dataを学ぶ](https://zenn.dev/nao_randd/articles/24f94ba78bc15b)
そしてここで[Editor]->[Create NSManagedObject Subclass..]を選択します。

![スクリーンショット 2023-11-29 21.14.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058061/6ff38ad8-67b4-db49-ba23-c5abce80c3ea.png)
するとこの画面に移るのでひたすら[Next]->[Next]->[Create]
![スクリーンショット 2023-11-29 21.14.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058061/fe5c9276-5476-8804-24e3-c275f0ff7656.png)
をすると
Memo+CoreDataClass.swift
Memo+CoreDataProperties.swiftが追加されます。
この辺の詳細もわかりやすい記事を貼っておきますので気になるかたはこちらから
[やっとわかったSwift/CoreData入門 ](https://qiita.com/pe-ta/items/89203ccc72d964277818)
次にPersistence.swiftの17,18行目を編集します。
新しくプロジェクトを作った方は
```
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
```
となっていると思いますので
```
             let newEntity名 = Entity名(context: viewContext)
             newEntity名.Attribute名 = 初期値
             //Memoエンティティで2つ以上のデータを扱う場合はこの下に同じように書く
```
と変更しておきます。
よって今回は
```
            let newMemo = Memo(context: viewContext)
            newMemo.memo = ""
```
とします。
最後に
プロジェクト名App.swiftに環境値として
***
##### managedObjectContext
役割　・Entityのインスタンスを生成、管理
　　　・persistanceStore(データベースの情報管理クラス)に変更を保存
* * *

をセットしておきます。
```diff_Swift
import SwiftUI

@main
struct QiitaCoreDataApp: App {
+        let persistenceController = PersistenceController.shared

    var body: some Scene {
        WindowGroup {
            ContentView()
+                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}

```
これでセットアップは完了です！簡単なデータのみですがかなり簡単にセットアップができたと思います。

では次はデータの操作(取得、追加、削除)をしていこうと思います！
## データ操作編
まず、CoreDataを利用したいViewに
managedObjectContextを **ViewContext** として
定義しておきます
```
 @Environment(\.managedObjectContext) private var viewContext
```
#### 取得
データの取得は
@FetchRequestを使い、プロパティの
**keyPath**で基準とするデータを特定し,
**ascending**でデータを昇順や降順にソートすることができます。
FetchedResultプロパティを使うことでデータを取得することができ、
**memoList**という名前で利用することができます。
今回は
```swift:ContentView.swift
 @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Memo.memo, ascending: true)],
        animation: .default)
        private var memoList: FetchedResults<Memo>
```
とします。これでmemoListをfor文などで回して表示することができます。
***
#### 追加
データの追加は
```swift
    private func addItem(memoString: String) {
        withAnimation {
            //Memoエンティティのインスタンスを初期化
            let newMemo = Memo(context: viewContext)
            newMemo.memo = memoString

            do {
                //データを保存
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
```
で追加することができます。
```
try viewContext.save()
```
を忘れないようにしましょう。

***
#### 削除
削除はdeleteItemsを使って削除します。
今回はonDeleteを使うのでoffSetからデータを特定して削除します。
```swift
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { memoList[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
```
***
これらを用いると
![screen.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058061/b0d2580d-c0f2-e78c-8327-42bd97af0587.gif)

このようなデータの取得、追加、削除ができるアプリを作ることができます。
ContentViewの全体を載せておきます。
```swift:ContentView.swift


//
//  ContentView.swift
//  QiitaCoreData
//

import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Memo.memo, ascending: true)],
        animation: .default)
    private var memoList: FetchedResults<Memo>
    @State private var memoString: String = ""

    var body: some View {
        VStack {
            HStack {
                TextField("メモを入力", text: $memoString)
                    .frame(width: 300, height: 60)
                Button(action: {addItem(memoString: memoString)}, label: {
                    Text("追加")
                })
            }
                List {
                    ForEach(memoList) { item in
                        Text(item.memo ?? "")
                    }
                    .onDelete(perform: deleteItems)
                }

        }
        }


    private func addItem(memoString: String) {
        withAnimation {
            //Memoエンティティのインスタンスを初期化
            let newMemo = Memo(context: viewContext)
            newMemo.memo = memoString

            do {
                //データを保存
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { memoList[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
    }
#Preview {
    ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}

```
**import CoreData**も忘れずに。
https://github.com/sawakishuto/qiitaCoreData

## 終わりに
#### 爆速CoreData 
いかがだったでしょうか、爆速で使えるようになりましたか？
とても単純な操作ではありますが、CoreDataの扱いの理解の手助けになっていれば嬉しいです。
また、登場クラスが多くあり、今回は説明を端折っている部分が多いので一つ一つの意味を深ぼると、より理解が深まるかと思います。
自分自身あまり理解できていない部分も多かったので、今回この記事を書いていて気づく点が多くとても良い機会になりました。
至らない点もあると思いますので、間違い等ありましたらぜひコメント等で訂正していただけると嬉しいです。
***
### ちょっと宣伝
先日、個人開発していたアプリ
**MoToBBS**
をリリースすることができました！！！
ちょい乗り、キャンプツーリング、ミーティングなど、バイクのツーリングが誰でもどこでも手軽に募集、参加できるというアプリになっております！
ちなみにこのアプリにも
**・自動ログインのためのログイン情報**
**・チュートリアル**
**・ブロックリスト**
等にCoreDataを使用しています！
これからも機能追加、改善しますのでぜひダウンロードしてみてください！

ダウンロードはこちらから
↓
[MoToBBS](https://apps.apple.com/jp/app/motobbs/id6469105461)