(2021/04/26 更新)
Xcodeでエンティティを定義する方法を解説します。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.3.2
【iOS】14.4
【macOS】Big Sur バージョン 11.1
エンティティとは
Core Dataにおけるエンティティとは、DBのテーブルをクラスで表したもので、テーブルが持つ属性やリレーションなどの情報を持ちます。
Core DataではDBのレコードをオブジェクトとして扱う為、このクラス定義が必要となります。
エンティティを定義する
エンティティの定義には、XcodeのModel Editor(拡張子が.xcdatamodeld)を使用します。
Model Editorはプロジェクト作成時にUse Core Dataチェックを入れると自動で生成されますが、後から手動でファイル追加も可能です。
エンティティの生成方法
- 左のProject navigatorでModel Editorを選択
- 画面下部のAdd Entityボタンのクリックで、エンティティ(Entity)が追加される
- 追加されたEntityをクリックして名称を変更する(1文字目は大文字必須)
エンティの削除方法
- 対象のエンティティを選択した状態でキーボードのDeleteキーを押す
属性情報の変更方法
- エンティティを選択すると、右のAttributesに対象エンティティの属性情報が表示される
- Attributes下の「+」ボタンで属性追加、「ー」ボタンで属性削除
- 属性名のクリックで名称変更が可能(1文字目は小文字必須)
- Typeを選択すると属性のタイプが変更可能
属性名について注意点
属性名には、NSObjectやNSManagedObjectが持つメソッドやプロパティと重なる名前が使えません。
例えば"class"や、"className"などを指定すると、次のようなダイアログ(警告)が表示されます。
"Property name clashes with a method implemented by NSManagedObject or NSObject"
エンティティのクラスファイル生成について
定義したエンティティに対応するクラスファイルは、デフォルトの設定ではビルド時に自動生成されます。
その為、ビルド前にコードで該当クラスを使う記述があると、次のようにエラー"Cannot find type 'xxxxx' in scope"が発生する場合がありますが、一度ビルドを実行すると解消します。
コード生成オプション
エンティティのコード生成オプション(Codegen)で、クラスファイルの生成方式を選択可能です。
Model Editorで対象のエンティティを選択し、属性インスペクタを開くとコード生成オプション(Codegen)の設定を確認/変更できます。
選択できるオプションは次の3つです。
Class Definition(デフォルト設定)
エンティティに対応したクラスをビルド時に自動生成します。
ソースコードは生成されませんので、コードによるカスタマイズが不要の場合は、こちらを選択します。
手動で作成したコードが存在しているとビルド時に重複エラー"Multiple commands produce ..."となりますので注意して下さい。
Manual/None
エンティティに対応したクラスをビルド時に自動生成しません。
こちらを選択した場合は後述する方法でコードを生成する必要があります。
必要なファイルは次の2つ
エンティティ名+CoreDataClass.swift
エンティティに固有の実装を定義するファイル。
エンティティ名+CoreDataProperties.swift
エンティティの属性情報を定義するファイル。
Category/Extention
エンティティに対応した2つのコードのうち、エンティティの固有実装を定義する"エンティティ名+CoreDataClass.swift"をビルド時に自動生成しません
属性情報はModel Editor上で設定し、固有の実装のみコードで記述したい場合にはこちらを選択します。
コードの手動生成方法
Model Editorを開き対象のエンティティを選択した状態で、メニューより Editor > Create NSManagedObject Subclass...を選びます。
1.対象のデータモデルを選択
「Next」を選択でOK
2.対象のエンティティ(Entity)を選択
コードを生成したい、エンティティをチェックして「Next」
3.フォルダの選択
コードを格納するフォルダを選択して「Create」
※画面右側の上部が実フォルダの場所、下部のGroupがXcodeのProject navigtorでの表示位置を示します。
生成されるコード
次の2種類のコードが生成されます。
エンティティ名+CoreDataClass.swift
エンティティに固有の実装を定義する為のファイルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// // Student+CoreDataClass.swift // SampleProject // // Created by capibara1969 on 2021/03/16. // // // import Foundation import CoreData @objc(Student) public class Student: NSManagedObject { } |
エンティティ名+CoreDataProperties.swift
エンティティの属性情報を定義するファイルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// // Student+CoreDataProperties.swift // SampleProject // // Created by capibara1969 on 2021/03/16. // // // import Foundation import CoreData extension Student { @nonobjc public class func fetchRequest() -> NSFetchRequest<Student> { return NSFetchRequest<Student>(entityName: "Student") } @NSManaged public var absentDays: Int16 @NSManaged public var birthday: Date @NSManaged public var club: String? @NSManaged public var name: String @NSManaged public var nameOfClass: String @NSManaged public var sid: String } extension Student : Identifiable { } |
属性情報のoptional問題
Student+CoreDataProperties.swiftの次のコードに注目しましょう。
1 2 3 4 5 6 7 8 |
@NSManaged public var absentDays: Int16 @NSManaged public var birthday: Date? @NSManaged public var club: String? @NSManaged public var name: String? @NSManaged public var nameOfClass: String? @NSManaged public var sid: String? |
これはStudentエンティティの各属性の型が定義されている箇所ですが、数値のabsentDays以外はデフォルトでオプショナルとなっています。
この為、データベースの検索結果をコードで扱う場合、対象属性のアンラップ処理や、nil結合演算子(??)によるデフォルト値の設定など煩雑な手続きが必要となります。
nilが入らないことが確実な場合は、単純にオプショナルを外すと一見うまく動くように見えますが、Core Dataの仕様上実際には開発者の意図に沿わずnilになるケースがある為お勧めしません。
その代わりに、オプショナル値に安全にアクセスするのに役立つ、計算プロパティの追加の検討すると良いでしょう。
対象属性がnilだった場合のデフォルト値定義を1箇所に集め、残りのコードではオプション性を気にする必要がなくなります。
1 2 3 4 5 6 7 8 9 10 11 12 |
extension Student { /// 値がnilの場合のデフォルト値定義 public var wrappedBirthday: Date { birthday ?? Date() } public var wrappedClub: String { club ?? "" } public var wrappedName: String { name ?? "" } public var wrappedNameOfClass: String { nameOfClass ?? "" } public var wrappedSid: String { sid ?? "" } } |