【Xcode/SwiftUI】CoreDataを使ってみる

アプリを終了してもデータを残すためのものです.

実装

DataModelの追加

Command + Nで新規ファイル追加画面を開き、DataModelを選択する

CoreDataSample.xcdatamodeldと名付けて、中身に新たなEntityを加える

Persistence.swift

Persistence.swiftというファイルを追加、以下をそのままコピペ

import CoreData

// Core Data stackの作成、管理を行う.
struct PersistenceController {
    // アプリ全体で使えるShared Instance.
    static let shared = PersistenceController()

    // プレビュー、テスト用
    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext

        // サンプルを10個 in-memoryストアに保存.
        for _ in 0..<10 {
            let newItem = Task(context: viewContext)
            newItem.name = "Sample Task"
        }

        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: "CoreDataSample")

        if inMemory {
            if let firstStoreDescription = container.persistentStoreDescriptions.first {
                firstStoreDescription.url = URL(fileURLWithPath: "/dev/null")
            } else {
                fatalError("Unable to access persistent store description")
            }
        }

        // ロード
        container.loadPersistentStores { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        }
    }
}

HomeView.swift (メインのView)

import SwiftUI

struct HomeView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(entity: Task.entity(), sortDescriptors: []) var tasks: FetchedResults<Task>
    @State private var taskName = ""

    var body: some View {
        NavigationView {
            List {
                Section(header: Text("Add Task")) {
                    HStack {
                        TextField("Enter task name", text: $taskName)
                        Button(action: addTask) {
                            Text("Add")
                        }
                    }
                }

                Section(header: Text("Tasks")) {
                    ForEach(tasks, id: \.self) { task in
                        Text(task.name ?? "")
                    }
                    .onDelete(perform: deleteTasks)
                }
            }
            .listStyle(GroupedListStyle())
            .navigationTitle("Task List")
            .navigationBarItems(trailing: EditButton())
        }
    }

    private func addTask() {
        withAnimation {
            if !taskName.isEmpty {
                let newTask = Task(context: viewContext)
                newTask.name = taskName
                taskName = ""
                try? viewContext.save()
            }
        }
    }

    private func deleteTasks(offsets: IndexSet) {
        withAnimation {
            offsets.map { tasks[$0] }.forEach(viewContext.delete)
            try? viewContext.save()
        }
    }
}

struct HomeView_Previews: PreviewProvider {
    static var previews: some View {
        HomeView()
            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

アプリ名.swift

import SwiftUI

@main
struct SwiftUI_PlaygroundApp: App {
    let persistenceController = PersistenceController.shared // 追加
    
    var body: some Scene {
        WindowGroup {
            HomeView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext) // 追加
        }
    }
}