【Swift】Core Dataの設定。カスタムクラスを作ってエンティティの属性へのアクセスを簡潔にする。(Swift 2.1、XCode 7.2)
Entityの設定
本記事は、前回記事「Entityの設定」の続きである。⇒「前回記事」
Class
管理オブジェクトコンテキストが管理するデータはNSManagedObjectクラスのインスタンスとしてメモリ上に展開される。このNSManagedObjectクラスを継承したクラスをカスタムクラスとして設定することができる。
カスタムクラスを使う大きな利点は属性値へのアクセスが簡潔に記述できることだ。
例えば、カスタムクラスを使わない場合、オブジェクトの属性値にアクセスするには以下のコードのように属性名の文字列を与えるが、
1 2 3 |
//カスタムクラスを使ってないときの属性値へのアクセス player.setValue(testTextField.text, forKey:"name") |
カスタムクラスを使うと、以下のコードのようにインスタンス変数のプロパティとして属性値にアクセスすることができる。
記述がシンプルになるし、プロパティ名を間違ったらコンパイルエラーで教えてくれるので、こちらの方が安心してコーディングできる。
1 2 3 |
//カスタムクラスを使っているときの属性値のアクセス player.name = testTextField.text |
実際にカスタムクラスを作ってみよう。
以降の手順を開始する前のXcodeプロジェクトをGitHubに置いたので、試してみる人はご利用されたし。
⇒「テスト用プロジェクト」
事前準備では、テキストフィールドの文字列をCore Dataで保存、読込みするものを実装しておいた。
下図赤枠の「プロジェクト名.xcdatamodeld」を選択したあと、メニュー⇒「Editor」⇒「Create NSManagedObject Subclass」とたどる。
データモデルの一覧が表示されるので、「プロジェクト名」にチェックが入っていることを確認して「Next」ボタンを押す。
データモデルに定義されているEntityの一覧が表示されるので、Playerにチェックが入っていることを確認して「Next」ボタンを押す。
保存先を指定する画面が表示されるので、プロジェクトのフォルダが指定されていることを確認して「Create」ボタンを押す。
すると、「Player.swift」と「Player+CoreDataProperties.swift」の2つのファイルが作られる。これがカスタムクラスのファイルである。
Player.swiftは、NSManagedObjectクラスを継承したPlayerクラス。最初はプロパティやメソッドは何もない。Playerクラス固有の実装をここにコーディングしていく。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// // Player.swift // import Foundation import CoreData class Player: NSManagedObject { // Insert code here to add functionality to your managed object subclass } |
Player+CoreDataProperties.swiftは、Playerクラスを拡張したクラス。⇒「拡張とは」
ここにエンティティの属性がプロパティとして定義される。なお、Playerエンティティの定義を変更して再度サブクラスを作ると、「Player+CoreDataProperties.swift」が更新され、「Player.swift」は更新されない。
2つのファイルが存在する理由は、エンティティの定義を変更したあとのサブクラス再生成で開発者がコーディングした実装箇所が上書きされないためである。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// // Player+CoreDataProperties.swift // import Foundation import CoreData extension Player { @NSManaged var age: NSNumber? @NSManaged var name: String? } |
下図赤枠の「プロジェクト名.xcdatamodeld」を選択、黄緑枠の「Player」を選択、紫枠のデータモデルインスペクタボタンを押して設定画面を表示し、Classの設定を確認すると、作成したカスタムクラス名が自動で設定されていることが分かる。親切に設定してくれてありがとう。
ViewController.swiftを以下のコードに変更する。
管理オブジェクトコンテキストから取得したオブジェクトをPlayerクラスにキャストし、nameプロパティに対して値を設定、取得するようにした。
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
// // ViewController.swift // import UIKit import CoreData class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet weak var testLabel: UILabel! @IBOutlet weak var testTextField: UITextField! //管理オブジェクトコンテキスト var managedContext:NSManagedObjectContext! //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() do { //管理オブジェクトコンテキストを取得する。 let applicationDelegate = UIApplication.sharedApplication().delegate as! AppDelegate managedContext = applicationDelegate.managedObjectContext //管理オブジェクトコンテキストからPlayerエンティティを取得する。 let fetchRequest = NSFetchRequest(entityName: "Player") let result = try managedContext.executeFetchRequest(fetchRequest) as! [Player] //すべてのPlayerエンティティの名前をラベルに表示する。 for data in result { testLabel.text = testLabel.text! + "," + data.name! } //デリゲート先に自分を設定する。 testTextField.delegate = self } catch { print(error) } } //Returnキー押下時の呼び出しメソッド func textFieldShouldReturn(textField:UITextField) -> Bool { do { //ラベルの値にテキストフィールドの値を追記する。 testLabel.text = testLabel.text! + "," + testTextField.text! //新しいPlayerエンティティを管理オブジェクトコンテキストに格納する。 let player = NSEntityDescription.insertNewObjectForEntityForName("Player", inManagedObjectContext: managedContext) as! Player //Playerエンティティの名前にテキストフィールドの値を設定する。 player.name = testTextField.text //管理オブジェクトコンテキストの中身を永続化する。 try managedContext.save() //キーボードをしまう self.view.endEditing(true) } catch { print(error) } return true } } |
以下は実際のプレイ動画。属性へのアクセス方法を変えただけなので、動きは変更前と同じである。
Module
Classで指定したカスタムクラスが存在するモジュールを指定する。モジュールとは、プロジェクトをビルドしたものやフレームワークのことをいう。
現在のプロジェクトの中にカスタムクラスを作る場合は「Current Product Module」に設定する。Classと同様に、カスタムクラスを作ったときにこの項目も自動で設定される。