【Swift】Core Dataの使い方。編集データを外部ファイルに保存する。これを使えば大量データも怖くない。(Swift 2.1、XCode 7.2)
Core Dataとは
本記事ではSwiftで使える機能のCore Dataについて説明する。
Core Dataとは、アプリで入力または編集したデータを外部ファイルに保存する機能である。
過去の記事で説明したNSUserDefaultも外部ファイルに保存する機能だが、大量または複雑なデータを扱うにはCore Dataを使うのが適している。⇒「NSUserDefaultとは」
データ保存の大まかなイメージを下図で説明すると、まずアプリで入力または編集したデータをオブジェクトとして管理オブジェクトコンテキストという入れ物に入れる。
一通り入れ終わったら、管理オブジェクトコンテキストに対して保存要求を行う。すると、オブジェクトの構造が定義されているモデルを使って形式が変換され、外部ファイルに出力される。これが保存の流れになる。
逆の外部ファイルのデータを読み込む流れは、管理オブジェクトコンテキストに対してデータ取得要求を行うと、モデルを通して必要なデータのみが管理オブジェクトコンテキストに読み込まれ、それをアプリが参照する。
必要なデータの絞り込みをしっかりすれば、大量データが外部に保存されていてもメモリを逼迫することがないのがCore Dataの良いところだ。
Core Dataを使ってみる
Core Dataを使った簡単なプログラムを実装してみよう。
Core Dataを使うにはプロジェクトを作るときのオプション選択で「Use Core Data」にチェックを入れる。チェックを入れなくても使うことはできるが、チェックを入れると使うための下準備をしてくれる。
Core Dataを使おうとしている人でプロジェクトの作り方を知らない人はいないと思うが、念のためプロジェクトの作り方は次の記事を参照されたし。⇒「プロジェクトの作り方」
下図は、「Use Core Data」のチェック有無で作られるファイルの違いを比較したもの。チェック有りの方は「プロジェクト名.xcdatamodeld」ファイルが最初から存在する。これは、先ほど説明したモデルを定義するためのファイルである。
その他の違いは、AppDelegate.swiftにCore Dataを使うためのプロパティとメソッドが追記されている。
ラベル(下図赤枠)とテキストフィールド(青枠)をデバイス画面に配置する。黄緑枠のアシスタントエディタボタンを押してViewController.swiftを開く。
Ctrlキーを押しながらラベルをドラッグ&ドロップでソースコードまで運んで吹き出しの設定画面を表示させる。Connectionに「Outlet」、Nameに「testLabel」を入力して、Connectボタンを押す。
同じようにしてテキストフィールドの吹き出しの設定画面を表示させ、Connectionに「Outlet」、Nameに「testTextField」を入力して、Connectボタンを押す。これでラベルとテキストフィールドをソースコードから操作できるようになった。
まずはCore Dataを使わない実装から。
ViewController.swiftを以下のコードに変更する。Returnキーを押すと、テキストフィールドに入力した文字列をラベルに追記するようにした。
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 |
// // ViewController.swift // import UIKit class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet weak var testLabel: UILabel! @IBOutlet weak var testTextField: UITextField! override func viewDidLoad() { super.viewDidLoad() //デリゲート先に自分を設定する。 testTextField.delegate = self } //Returnキー押下時の呼び出しメソッド func textFieldShouldReturn(textField:UITextField) -> Bool { //ラベルの値にテキストフィールドの値を追記する。 testLabel.text = testLabel.text! + "," + testTextField.text! //キーボードをしまう self.view.endEditing(true) return true } } |
以下は実際のプレイ動画。データの保存や読込みをしてないのでアプリを再起動するとラベルの文字列が初期値に戻る。
次はCore Dataを使う実装。モデルの定義から始める。
下図赤枠の「プロジェクト名.xcdatamodeld」を選択すると右側にモデルに定義されているエンティティが表示される。
エンティティとは、オブジェクトが持つプロパティや関係が記載されている定義書のことで、管理オブジェクトコンテキストに格納するオブジェクト1つにつき1つのエンティティが割り当たる。
Core Dataはモデルに記載されているエンティティを用いてオブジェクトから外部ファイルへ、外部ファイルからオブジェクトへの形式変換をしているのだ。
といってもまだエンティティを作っていないので、エンティティは何もない。
黄緑枠のAdd Entityボタンを押すと、紫枠のEntityが追加される。紫枠のEntityを選択、水色枠のデータモデルインスペクタボタンを押して設定画面を表示し、Nameに「Player」を入力する。これはソースコードからエンティティにアクセスするための識別子になる。
続けて、下図赤枠の「+」ボタンを押してAttributesの行を追加し、Attributeに「name」、Typeに「String」を入力する。もう一度赤枠の「+」ボタンを押して行を追加し、Attributeに「age」、Typeに「Integer 16」を入力する。
これでPlayerの持つ属性に名前と年齢が追加された。
ViewController.swiftを以下のコードに変更する。
viewDidLoadメソッドで、管理オブジェクトコンテキストからすべてのPlayerエンティティを取得し、ラベルに名前を列挙している。
デリゲートメソッドの「Returnキー押下時の呼び出しメソッド」では、新しいPlayerエンティティを管理オブジェクトコンテキストに格納したあと、外部ファイルへの保存要求(saveメソッド)を行っている。
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! [NSManagedObject] //すべてのPlayerエンティティの名前をラベルに表示する。 for data in result { testLabel.text = testLabel.text! + "," + String(data.valueForKey("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) //Playerエンティティの名前にテキストフィールドの値を設定する。 player.setValue(testTextField.text, forKey:"name") //管理オブジェクトコンテキストの中身を永続化する。 try managedContext.save() //キーボードをしまう self.view.endEditing(true) } catch { print(error) } return true } } |
以下は実際のプレイ動画。Core Dataを使って保存したデータが再起動時にラベルに表示されるようになった。
次回以降もしばらくCore Dataの話を続ける。