[Swift] core dataの基本的な実装をまとめてみた

#はじめに
core dataについて色々調べ、とりあえず実装できた状態です。
自分なりにまとめてみます。
tableviewcontrollerと、遷移先の項目を追加するview２つを作成します。
core data以外の操作は省略します🙇‍♂️

#実装と解説
###1 プロジェクト作成
まずはじめにプロジェクトを立ち上げる際に、User　Core　Dataを選択します。
すると、ファイル名.xcdatamodeldというファイルが追加されます、

###2 tableviewcontrollerと追加用のviewcontrollerの実装
イメージはこんな感じです。
![スクリーンショット 2021-01-16 21.04.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755375/2e4a1bbe-468d-3296-c818-4f61ec791707.png)

###Core Dataの設定
①のファイルを選択すると、Core Dataの設定ができます。
②でEntityを追加します。
③が追加されます。Entityは今回データを入れておいて保管する場所になります。
今回はメモ内容の保存に使いますので、Memoという名前にしてみます。
Entityは大文字からの名前をつけるのが無難だそうです。
![スクリーンショット 2021-01-16 8.04.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755375/ed84eed5-1df6-04a1-0134-b331fd42c263.png)
④の＋を押すと、⑤のAttributeがが追加できます。これは今回追加するデータになります。
今回はメモの内容を保存するnameをstring型、チェックマークの状態を保存するcheckをBoolean型で設定します。
型はTypeから設定できます。
![スクリーンショット 2021-01-16 8.16.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755375/15cd910e-2756-e116-774e-f4ccff6922e5.png)
各項目のOptionalのチェックを外しておいてください。
ここで一旦ビルドしておきます。しておかないと後々エラーになるそうです。
![スクリーンショット 2021-01-16 8.25.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755375/6179bd60-2032-a199-2521-531f671e2819.png)

#コード
###保存の処理
①`viewContext`は変更などを見にいき、操作もできるメソッドで、それを定数contextに入れる
②そのcontextをMemo（context: context）に入れる事で、Core DataであるMemoの変更や操作ができるようになる。
③それを定数memoに入れたので、memo.attributeの項目を操作できる
④最後にsaveContext()でデータを保存して完了！



```swift
import UIKit

class AddViewController: UIViewController {

    @IBOutlet weak var addTextField: UITextField!
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func addButton(_ sender: Any) {
// persistentContainerは総監督の役割でcore dataに関連するNSManagedObjectContext・
// NS PesistentStoreCoordinator・NSpersistentStoreに指示を出せる。
// 以下のcontextはManagedObjectContextへの参照が含まれている。
// viewContextはNSManagedObjectContext型で、管理されているオブジェクトを操作したり、
// 変更を追跡したりする。
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// (context:)はモデル内の単一のエンティティを表すNSManagedObject（この場合はMemo）のサブクラスに対してのみ、
// 合法的に呼び出すことができる。この場合、Memoの中で変更があるかとかをcontextで追跡し、
// それを呼び出して定数に入れているイメージかな？ManagedObjectのサブクラスを初期化し、
// 指定されたNSManagedObjectContextに挿入できるようになる。
        let memo = Memo(context: context)
//memoはManagedObjectのサブクラスを初期化し、
//指定されたNSManagedObjectContextに挿入できるようになったので、
//以下のように値などを入れる事ができる。
        memo.name = addTextField.text
        memo.check = false
        // 変更があれば保存する処理
        (UIApplication.shared.delegate as! AppDelegate).saveContext()
        navigationController!.popViewController(animated: true)
    }
}
```

###データを取得して、表示するコード

```swift
import UIKit

class MemoTableViewController: UITableViewController {
    
    // 変数memosを用意して、core dataのMemo型を追加していくためにMemo型に指定する
    var memos : [Memo] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: TableViewCell.reuseIdentifier)
    }
    
    override func viewWillAppear(_ animated: Bool) {
// データを取得する
        getData()
        tableView.reloadData()
    }

    // MARK: - Table view data source

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return memos.count
    }

    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.reuseIdentifier, for: indexPath) as! TableViewCell
        
        let memo = memos[indexPath.row]
        cell.configure(isCheck: memo.check, name: memo.name!)
        
        return cell
    }
    
    func getData() {
        // 変更箇所を見にいく
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        do {
            // あれば取得
            memos = try context.fetch(Memo.fetchRequest())
        }
        catch {
            print("読み込み失敗")
        }
    }
}
```
###cellの内容

```swift
import UIKit

class TableViewCell: UITableViewCell {
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var checkImage: UIImageView!
    
    static let image = UIImage(named: "check")
    
    static let reuseIdentifier = "Cell1"
    
    func configure(isCheck: Bool, name: String) {
// checkマークの表示の処理
        checkImage.image = isCheck ? TableViewCell.image : nil
        label.text = name
    }    
}
```

###データの内容を削除
editingStyle内で処理を書きます。
また同じように、viewContextで操作できるようにします。
`context.delete`で削除できます。
あとは削除した状態を`saveContext（）`で保存してあげれはOKです。

```swift
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        if editingStyle == .delete {
            let memo = memos[indexPath.row]
            context.delete(memo)
            (UIApplication.shared.delegate as! AppDelegate).saveContext()
            do {
                // あれば取得
                memos = try context.fetch(Memo.fetchRequest())
            }
            catch {
                print("読み込み失敗")
            }
        }
        tableView.reloadData()
    }
```

###checkマークの切り替え
！を先頭につける事で値が反転できます。
これをdidSeleceRowAtに書く事で、cellのタップ時に切り替える事ができます。

```swift
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let memo = memos[indexPath.row]
        memo.check = !memo.check
        tableView.reloadData()
    }
```

#最後に
まだまだ理解できていないところがありますが、実装段階での理解の手助けになれば幸いです。
間違っている箇所などありましたら遠慮なくご指摘ください。
#参考サイト
https://blog.codecamp.jp/programming-iphone-app-development-todo