7
9

More than 3 years have passed since last update.

【新人プログラマ応援】この素晴らしいSwiftUIにCoreDataを!【CoreData読み書き編】

Last updated at Posted at 2021-03-23

こんにちは.
Sadmachineです.

この記事ではアプリ開発の基礎...
CoreDataの読み書きをわかりやすく解説していきます!
スクリーンショットも適宜つけるので参考にしてください!

前回の記事では
Xcodeプロジェクトの開始〜CoreDataの初期設定までをわかりやすく解説しました!
前回の記事:この素晴らしいSwiftUIにCoreDataを!【初期設定編】

LGTMたくさんしてね!!!

目次

おさらい : 0. 開発環境

筆者の開発環境をめっちゃ簡単に紹介します.

  • Xcode 12.3
  • SwitfUI

以上.
もっと詳しく知りたい人はTwitterに凸して,どうぞ.

3. CoreDataの読み書きをしてみよう

前回の記事:この素晴らしいSwiftUIにCoreDataを!【初期設定編】の設定のままではエラー吐かれがちなので修正します.
※以下のソースのMugenMemoは適宜自分のProductNameに読み替えて進めてください.

3-1 CoreDataを動かすためのエラー修正

以下の2つのコードを修正します.
Persistence.swift (絶対修正が必要)
MugenMemoApp.swift (場合によっては修正が必要)
ほとんどの場合,Persistence.swiftの修正のみで済むと思います.

Persistence.swiftの修正

スクリーンショット 2021-03-22 23 18 32
↑図の16~19行目を削除またはコメントアウトするだけです.

Persistence.swiftは以下のようにすればOK.

Persistence.swift
import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
//
//ここら辺にあるfor文をコメントアウトにするか削除
//
        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentCloudKitContainer

    init(inMemory: Bool = false) {
        container = NSPersistentCloudKitContainer(name: "MugenMemo")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                Typical reasons for an error here include:
                * The parent directory does not exist, cannot be created, or disallows writing.
                * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                * The device is out of space.
                * The store could not be migrated to the current model version.
                Check the error message to determine what the actual problem was.
                */
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
    }
}

MugenMemoApp.swiftの修正

もし,ProductNameApp.swiftでエラーを吐かれたら
.environment(.managedObjectContext, persistenceController.container.viewContext)
の部分に色が付いていないことが原因であることが多いです.
.environmentの1行を切り取り,元あった場所に貼り付ければ.environment部分に色がつき,大体解決します.

一応コードも以下に載せておきます.

MugenMemoApp.swift
import SwiftUI

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

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

以上でエラーを吐かないで動いてくれるようになります.

3-2 CoreDataにデータを追加

流れとしては
1. CoreDataにデータを書き込む準備
→ SwiftUIでCoreDataにデータを追加する「+ボタン」を実装
2. CoreDataにデータを書き込む・読み込んで表示させる
→ +ボタンを押すとCoreDataにデータを追加する処理を実装
→ ContentView.swiftにList形式でCoredataの中身を表示させる処理の実装
3. エミュレーターでテストして動作を確認する

それではやっていきましょう.

CoreDataにデータを書き込む準備

→ SwiftUIでCoreDataにデータを追加する「+ボタン」を実装

この時点では押しただけでは何も起きないです.
もちろんCoreDataにも何も追加されません.
ContentView.swiftを以下のように編集します.

ContentView.swift
import SwiftUI
import CoreData

struct ContentView: View {
    // 編集箇所①
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Memo.title, ascending: true)],animation: .default) var Data: FetchedResults<Memo>

    var body: some View {
        NavigationView {
            List {
                Text("TEST")
            }
            // 編集箇所②
            .navigationTitle("Test")
            .navigationBarTitleDisplayMode(.inline)
            .navigationBarItems(trailing: Button(action: {
                                    // ここにアクションを書く
                                }) {
                                    Image(systemName: "plus")
                                }
            )

        }
    }

}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

保存してResumeを押すと...

スクリーンショット 2021-03-23 10 33 01
Canvas上でこうなるはずです.
こうなっていればOK!

CoreDataにデータを書き込む・読み込んで表示させる

→ +ボタンを押すとCoreDataにデータを追加する処理を実装
→ ContentView.swiftにList形式でCoredataの中身を表示させる処理の実装

以下がCoreDataに指定した文字列を追加し,Listに表示するコードです.

ContentView.swift
import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Memo.title, ascending: true)],animation: .default) var Data: FetchedResults<Memo>

    var body: some View {
        NavigationView {
            List {
                // Text("TEST")をForEach文に変更.
                // CoreDataの中身を表示している,
                ForEach(Data, id: \.self) { i in
                    Text(i.title!)
                }
            }
            .navigationTitle("Test")
            .navigationBarTitleDisplayMode(.inline)
            // +ボタンとCoreDataにデータを追加する処理を紐づける
            .navigationBarItems(
                trailing:
                Button(action: {
                                create()
                                }) {
                    Image(systemName: "plus")
                                }
            )
        }
    }

    // CoreDataにデータ(今回は文字列"I LOVE Swift")を追加する処理を実装
    func create() {
        let t = Memo(context: moc)
        t.title = "I LOVE Swift"
        do {
            try moc.save()
        } catch {

        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

スクリーンショット 2021-03-23 12 19 49
↑図のようにResumeを押した時のCanvasには何も表示されていませんが問題ありません.
ここからはエミュレーターで実行してみましょう.

エミュレーターでテストして動作を確認する

スクリーンショット 2021-03-23 12 19 49
赤く囲われた部分のiPhone11というところを押すとエミュレーターの機種が選べます.
選んだら左の再生ボタンを押してエミュレーター上でビルドしてみましょう.

スクリーンショット 2021-03-23 12 26 49
↑図のようになっていればエミュレーターの起動とアプリのビルドは成功です.
試しに+ボタンを押してみると...

スクリーンショット 2021-03-23 12 28 15

I LOVE Swiftという項目が追加されました.
試しにアプリを落としてみてもI LOVE Swiftは保持されたままです.
CoreDataへの追加は成功です!
やったぜ.

ちなみに,+ボタンを連打しまくるとI LOVE SwiftがListに量産されます.

まとめ

CoreDataの読み書きをわかりやすく解説しました.
今回はここまで.

次回は,

CoreDataのデータの削除とSwiftUIにおける削除UIの実装

の予定です.
SwiftUIの削除UIを導入してCoreDataのデータを削除できるようにします.
結構簡単に実装できるので次回もお付き合いください.

備忘録的なまとめですが,誰かの参考になれば嬉しいです.
少しでも参考になりましたらLGTMやフォロー,Twitterでの拡散をしてください!
喜びます.

7
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
9