【Swift】Core Dataの使い方。Fetched Propertyを使ってプロパティにフェッチ結果を持たせる。(Swift 2.1、XCode 7.2)
Fetched Propertyとは
本記事ではCore Dataの機能のFetched Property(フェッチドプロパティ)について説明する。
フェッチドプロパティとは、フェッチ結果のオブジェクトを保持するプロパティである。
モデルを定義するときのRelationshipの下に表示されている「Fetched Properties」(下図赤枠)でこの機能を実装する。
例えば、作家と作家の出版物を管理する場合で考えよう。
下図のように作家オブジェクトに名前、性別、年齢、出版物のプロパティを持たせる。外部ファイルに多くの著者の書籍データがあり、その中から杉田真の書籍データをフェッチして出版物プロパティに配列で持たせる。これがフェッチドプロパティである。
フェッチドプロパティを試す
実際にフェッチドプロパティを試してみよう。
以降の手順を行う前のXcodeプロジェクトをGitHubに置いたので、試してみる人はご利用されたし。
⇒「テスト用プロジェクト」
事前準備では、著者情報を表示する画面と、書籍データを追加削除する画面を作っておいた。「出版物読み込み」ボタンを押したらフェッチドプロパティを利用して書籍一覧が表示されるものをこれから実装する。
以下はViewController.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 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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
// // ViewController.swift // import UIKit import CoreData class ViewController: UIViewController { @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var sexLabel: UILabel! @IBOutlet weak var ageLabel: UILabel! @IBOutlet weak var itemTextView: UITextView! //管理オブジェクトコンテキスト var managedContext:NSManagedObjectContext! //検証用データ(著者) let authorList = [["杉田真",false, 32]] //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() //管理オブジェクトコンテキストを取得する。 let applicationDelegate = UIApplication.sharedApplication().delegate as! AppDelegate managedContext = applicationDelegate.managedObjectContext //検証用データを保存する。 insertData() //著者情報を表示する。 displayCustomer() } //検証用データを保存するメソッド func insertData(){ do { //著者オブジェクトの件数を取得する。。 let fetchRequest = NSFetchRequest(entityName: "Author") fetchRequest.resultType = .CountResultType let result = try managedContext.executeFetchRequest(fetchRequest) as! [Int] if(result[0] == 0){ //検証用の著者データを保存する。 for data in authorList { let author = NSEntityDescription.insertNewObjectForEntityForName("Author", inManagedObjectContext: managedContext) as! Author author.name = data[0] as? String //著者名 author.sex = data[1] as? Bool //性別 author.age = data[2] as? Int //年齢 } //管理オブジェクトコンテキストの中身を保存する。 try managedContext.save() } } catch { print(error) } } //著者表示メソッド func displayCustomer() { do { //オブジェクトを取得する。。 let fetchRequest = NSFetchRequest(entityName: "Author") let result = try managedContext.executeFetchRequest(fetchRequest) as! [Author] //ラベルに著者情報を設定する。 for author in result { nameLabel.text = author.name if(author.sex == true) { sexLabel.text = "女性" } else { sexLabel.text = "男性" } ageLabel.text = String(author.age!) } } catch { print(error) } } //出版物読込ボタン押下時の呼び出しメソッド @IBAction func pushButton(sender: UIButton) { } } |
まずはモデルにフェッチドプロパティを定義しよう。
下図赤枠の「プロジェクト名.xcdatamodeld」を選択、黄緑枠のAuthorエンティティを選択する。紫枠の「+」ボタンを押してフェッチドプロパティの行を追加する。
黄枠のデータモデルインスペクタボタンを押して設定画面を表示し、Nameに「publication」、Destinationに「Book」、Predicateに「author == $FETCH_SOURCE.name」を入力する。
これは「Bookエンティティのオブジェクトたちの中から、著者名と名前が一致するものをフェッチする」という条件設定である。
ViewController.swiftの「出版物読込ボタン押下時の呼び出しメソッド」を以下のコードに変更する。
出版物(フェッチドプロパティ)からフェッチ結果の配列を取り出し、テキストビューに一覧表示している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//出版物読込ボタン押下時の呼び出しメソッド @IBAction func pushButton(sender: UIButton) { //著者オブジェクトから出版物を取得する。 let bookArray = author.valueForKey("publication") as! [Book] //書籍をテキストビューに設定する。 var str = "" for book in bookArray { str += book.bookName! + "\n" } itemTextView.text = str } |
以下が実際のプレイ動画。「出版物読み込み」ボタンを押す度に、保存されているデータの中から杉田真の書籍のみが検索されていることが分かる。
NSArray型で取得した場合
1つ興味深い仕様がある。「出版物読込ボタン押下時の呼び出しメソッド」を以下のコードに変更して実行してみてほしい。
出版物の配列を取得するときにNSArray型にキャストして取得するようにした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//出版物読込ボタン押下時の呼び出しメソッド @IBAction func pushButton(sender: UIButton) { //著者オブジェクトから出版物を取得する。 let bookArray = author.valueForKey("publication") as! NSArray //書籍をテキストビューに設定する。 var str = "" for data in bookArray { let book = data as! Book str += book.bookName! + "\n" } itemTextView.text = str } |
以下は実際のプレイ動画。「出版物読込み」ボタンを押すと、保存されているデータの中から杉田真の書籍のみが検索されるのは先ほどと同じだが、それ以降はボタンを押しても1回目と同じデータしか取得できない。
これは、フェッチプロパティをNSArray型で取得すると、オブジェクト内部でNSArray型のフェッチ結果が保持される仕様のためである。
ただし、以下のコードでオブジェクトをリフレッシュすれば毎回最新の検索結果を取得できる。
1 2 3 |
//著者オブジェクトをリフレッシュする。 managedContext.refreshObject(author, mergeChanges: false) |
また、著者オブジェクトがどこからも参照されなくなってメモリから解放されたとき、フェッチ結果もクリアされる。なので、フェッチドプロパティを未来のデータ変更の影響を受けない、その時点のフェッチ結果を保持するプロパティとして使うことはできない。