【Swift/Core Data】NSManagedObjectのインスタンス作成方法!違いと使い方
この記事からわかること
- SwiftでCore Dataを利用する方法
- エンティティ(NSManagedObject)インスタンスの生成方法
- Class Definition、Manual/None、Category/Extensionの違い
- NSEntityDescriptionの使い方
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Xcode:15.0.1
- iOS:17.0
- Swift:5.9
- macOS:Sonoma 14.1
エンティティクラスが自動で生成される
Core Dataでは.xcdatamodeld
ファイルで新しくエンティティを定義すると自動でNSManagedObject
を継承したエンティティクラスファイルとプロパティファイルが生成されます。
生成されたファイル名はエンティティ名+CoreDataClass.swift
形式とエンティティ名+CoreDataProperties.swift
形式になるようです。
Person+CoreDataClass.swift
エンティティクラスファイルではCore Dataの管理対象オブジェクトであることを示すNSManagedObject
を継承しています。
@objc(Person)
public class Person: NSManagedObject {
}
Person+CoreDataProperties.swift
プロパティファイルではエンティティクラスを拡張してそのクラスのデータを取得するためのNSFetchRequest
を生成するメソッドや定義したプロパティが定義されています。
extension Person {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
return NSFetchRequest<Person>(entityName: "Person")
}
@NSManaged public var age: Int16
@NSManaged public var id: UUID?
@NSManaged public var name: String?
}
extension Person : Identifiable {
}
これら2つのファイルが自動生成されるのは.xcdatamodeld
ファイルでエンティティをアクティブにした状態でインスペクタエリアに表示されるCodegen
部分から設定を変更できます。初期値は自動生成されるClass Definition
になっており、Manual/None
、Category/Extension
の3つから選択することが可能です。
Class Definition
Class Definition
は2つのファイルを自動生成してくれる設定です。エンティティを登録後、最初にビルドされたタイミングで内部的に生成してくれます。
内部的に生成されるだけなので右側のファイルが表示されるナビゲータエリアには表示されません。実装する際にCannot find 'エンティティクラス' in scope
と出る場合は一度ビルドすれば解決するはずです。
定義ファイル自体を変更するたびに新しい内容のファイルに自動で更新されていくようです。
また自動生成されたクラスは編集することができないので処理を追加したい場合はManual/None
またはCategory/Extension
を選択する必要があります。
Category/Extension
Category/Extension
はプロパティファイルの自動生成はそのままにエンティティクラスファイルのみ手動で作成する設定です。生成するにはXcodeの上部メニュー「Editor」>「Create NSManagedObject Subclass.」をクリックし、生成したいエンティティを選択するだけです。
クラスファイルとプロパティファイルがナビゲータエリアに表示されるようになります。この方法で表示されているファイルは編集可能になっているので任意の処理を追加することが可能になります。
Category/Extension
ではプロパティファイルは自動生成されるのでナビゲータエリアに表示されているプロパティファイルと重複してしまうので明示的に削除しておく必要があります。
Manual/None
Manual/None
は自動生成をせずに2つのファイルとも手動で作成する設定です。手動で生成する手順はCategory/Extension
と同じくXcodeの上部メニュー「Editor」>「Create NSManagedObject Subclass.」をクリックし、生成したいエンティティを選択するだけです。
こちらは2つとも自動生成されないので削除する必要はありません。
エンティティクラスのインスタンス生成時のエラー
Core Dataで.xcdatamodeld
ファイルでエンティティを定義した後に、実際にデータを操作するためにインスタンス化しようとした際にそのままインスタンス化しようとするとエラーが出力されます。インスタンス化しただけではアプリはクラッシュしませんが、データベースに追加などを行うとアプリがクラッシュします。
let person = Person()
// CoreData: error: Failed to call designated initializer on NSManagedObject class 'Person'
インスタンス化する方法
エンティティクラスをインスタンス化する方法はいくつか存在するので違いと方法を紹介していきます。
init(context moc: NSManagedObjectContext)
公式リファレンス:init(context moc: NSManagedObjectContext)
1つ目はNSManagedObject
のイニシャライザinit(context moc: NSManagedObjectContext)
を使用する方法です。引数context
にはNSManagedObjectContext
を渡します。インスタンス化した段階でcontext
にはインサートされますが、save
をしない限り永続化はされません。
let person = Person(context: context)
init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
公式リファレンス:init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
NSManagedObject
の別のイニシャライザinit(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
を使用する場合はNSEntityDescription
を引数に渡す必要があります。
let entityDescription = NSEntityDescription.entity(forEntityName: String(describing: Person.self), in: context)!
let person = Person(entity: entityDescription, insertInto: nil)
引数insertInto
にNSManagedObjectContext
を渡すとデータベースに即座にデータを追加することができますがsave
をしない限り永続化はされません。エンティティだけを生成したい場合nil
を渡せばOKです。
NSEntityDescription.insertNewObject
公式リファレンス:NSEntityDescription.insertNewObject
class func insertNewObject(
forEntityName entityName: String,
into context: NSManagedObjectContext
) -> NSManagedObject
NSEntityDescription
のinsertNewObject
メソッドを使用してもインスタンスを生成することが可能です。ここで生成されるのはNSManagedObject
型なので任意のクラスにするためにはキャストする必要があります。
let entity = NSEntityDescription.insertNewObject(forEntityName: String(describing: Person.self), into: context)
let person = entity as! Person
ジェネリクスを使用して汎用性の高いインスタンスメソッドを実装する
エンティティは複数定義することも多いので使いまわしやすいようにジェネリクスを使用して以下のように実装することでエンティティクラスが増えてもコードを回収することなく利用することができます。
おすすめ記事:【Swift】ジェネリクスの意味と使い方!Comparableプロトコルとは?
/// 新規作成
public func newEntity<T: NSManagedObject>() -> T {
let entity = NSEntityDescription.insertNewObject(forEntityName: String(describing: T.self), into: context)
return entity as! T
}
インスタンス化する際は変数側に取得したいクラスの型を指定します。
let newPerson: Person = newEntity()
let newSchool: School = newEntity()
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。