LoginSignup
4
3

More than 3 years have passed since last update.

Xcodeプロジェクト作成後にCoreDataを有効にする。

Posted at

〜.xcdatamodeldファイルを生成する。

プロジェクト作成時にCoreDataを有効にしている場合は自動で作成されてます。後から追加する場合はこのファイルが必須になります。CoreDataはコマンドラインやxmlを使わずに、このフォームベースでデータベースの情報が登録できます。そのためには.xcdatamodeldが必須になります。

右クリックからNew Fileを選択して

スクリーンショット_2020-09-14_9_05_30.png

下にスクロールしてCore DataのData Modelを選択してください。

スクリーンショット_2020_09_14_9_08.png

保存したいデータの情報を登録する。

EntityとAttribute

Entityにはどんな情報を管理したいのかわかるような名前をつけます。タスク管理ならTask、メモ帳ならMemoなど、表の題名をイメージするとわかりやすいです。

Attributeには、Entityに保存する情報の単位とデータ型を決めます。タスクを新規追加した時間(Date)、タスク名(String)、タスクの有効/無効(Boolean)など、表の列名をイメージするとわかりやすい。

Model_xcdatamodel.png

NSManagedObjectを継承したクラスを作成する。

先程のCoreDataに作ったEntityとAttributeをSwiftからアクセスしてオブジェクトとして利用するためにNSManagedObjectを継承したデータクラスを作成します。

〜.xcdatamodeldを選択した状態でEditor->Create NSManagedObject Subclass...を選択して
案内通りに進みます。

スクリーンショット_2020-09-14_9_22_42.png

Task+CoreDataClass.swiftとTask+CoreDataProperties.swiftが生成されます。

ContentView_swift_と_「Xcodeプロジェクト作成後にCoreDataを有効にする。」を編集_-_Qiita.png

デフォルトの設定だと、NSManagedObjectを継承したクラスが存在しなくてもビルド時にxcodeが生成してくれますが、後々いろいろとやるために自分で作成して管理したほうが楽だと思いますので、この設定を無効にするためにcodegen(コードジェネレータ)をManual/Noneに変更します。

Model_xcdatamodel_と_「Xcodeプロジェクト作成後にCoreDataを有効にする。」を編集_-_Qiita.png

リスト形式でデータを表示するためにIdentifiableを継承させます。

Task+CoreDataProperties.swift

import Foundation
import CoreData


extension Task {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Task> {
        return NSFetchRequest<Task>(entityName: "Task")
    }

    @NSManaged public var id: UUID?
    @NSManaged public var name: String?
    @NSManaged public var isComplete: Bool
    @NSManaged public var dateAdded: Date?

}

//Identifiableに対応させるため追加
extension Task: Identifiable {
}

AppDelegate.swift

CoreDataライブラリを読み込ませ

import UIKit
import CoreData

AppDelegateクラスにpersistentContainerプロパティとsaveContextメソッドを追加

下のコードを追加します。



 lazy var persistentContainer: NSPersistentContainer = {

           let container = NSPersistentContainer(name: "Model")
           container.loadPersistentStores(completionHandler: { (storeDescription, error) in
               if let error = error as NSError? {                    
                   fatalError("Unresolved error \(error), \(error.userInfo)")
               }
           })
           return container
       }()

       func saveContext () {
           let context = persistentContainer.viewContext
           if context.hasChanges {
               do {
                   try context.save()
               } catch {
                   let nserror = error as NSError
                   fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
               }
           }
       }

NSPersistentContainerのnameに渡す文字列は.xcdatamodeldのファイル名になります。
自分が定義したファイル名に書き換えないとエラーになりますので注意してください。

Model.xcdatamodeldの場合

let container = NSPersistentContainer(name: "Model")

SceneDelegate.swift

CoreDataからデータを取得したり、新規データを追加するには、Contextが必要になります。ContentViewからContextを利用するためSceneDelegate.swiftを修正します。

let contentView = ContentView()

を下に変更


let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = ContentView().environment(\.managedObjectContext, context)

ContentView.swift


import SwiftUI
import CoreData

struct ContentView: View {

    @Environment(\.managedObjectContext) var context

    @FetchRequest(
        entity: Task.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Task.dateAdded, ascending: false)],
        predicate: NSPredicate(format: "isComplete == %@", NSNumber(value: false))
    ) var notCompletedTasks: FetchedResults<Task>

    @State var newTaskName:String = ""

    func addTask(taskName: String, context: NSManagedObjectContext) {
        let task = Task(context: self.context)
        task.id = UUID()
        task.isComplete = false
        task.name = taskName
        task.dateAdded = Date()
        do {
            try self.context.save()
        } catch {
            print(error)
        }
    }

    func completeTask(_ task: Task, context: NSManagedObjectContext){
      let taskId = task.id! as UUID
        print(taskId)
      let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Task")
      fetchRequest.predicate = NSPredicate(format: "id == %@", taskId as CVarArg)
      fetchRequest.fetchLimit = 1
      do {
        let tasks = try context.fetch(fetchRequest)
        let targetTask = tasks[0] as! NSManagedObject
        targetTask.setValue(true, forKey: "isComplete")
        try context.save()

        print(targetTask)
      } catch {
        print(error)
      }
    }

    var body: some View {

        VStack {
            TextField("タスクを入力してください。", text: $newTaskName, onEditingChanged: { _ in
            }, onCommit: {
                self.addTask(taskName: self.newTaskName, context: self.context)
                self.newTaskName =  ""
            })
                .keyboardType(.default)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding(32)

            List {
                ForEach(self.notCompletedTasks){ task in
                    Button(action: {
                        self.completeTask(task, context: self.context)
                    }) {
                        Text(task.name!)
                    }.onAppear() {
                        print(task.isComplete)
                    }
                }
            }

        }
    }
}

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

4
3
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
4
3