使いこなせればグンと開発の幅が広がりそうだけど、なんだか理解しにくいのがCoreData。データをアプリ側で保存して、ユーザー設定や次回のアプリ起動時まで残しておきたい情報をまとめて登録してくれる優れものなんですが、どうも面倒臭い。今回は簡単な組み込み方を学びながら、お手軽メモを作成してみたいと思います。
ベースは結構前に作ってあったのですが、Swift2.0仕様に直してたら時間を取られてしまいました。
早速やってみます
プロジェクトの新規作成
まずはプロジェクトを新規作成します。名前は”CoreMemoSample”としておきましょう。テンプレートは「Single View Application」を選択してください。今回はCore Dataのチェックが必要になります。手順の詳細は以下をご参照ください。
CoreDataの設定
最初にCoreデータの準備をします。Core Dataにチェックをしてプロジェクトを作成すると、左側のファイル一覧にCoreData用のファイルも追加されます。これを選択して、下にある「Add Entity」ボタンを押しましょう。Entityというのは保存するデータを格納する入れものだと思ってください。
次に今作ったEntityを選択して初期設定を済ませましょう。右上にある「Data Model Inspector」を選択して、NameとClassに”Memo”と入力し、Moduleの項目では「Current Product Modue」を選択してください。
続いてどんなデータを保存するか設定します。Attributesの項目にある「+」マークをクリックして、今回は入力されたテキストの内容を保存するだけなので、Attributeに”text”を入力、Typeで「String」を選択します。これで入れ物の初期設定ができました。
ここまでできたら、あと一息。今までの設定からクラスファイルを作成します。CoreData用のファイルをクリックして、メニューの「Editor」から「Create NSManagedObject Subclass」を選択します。するとファイルを作成する対象となるCoreDataモデルを指定する画面が表示されるので「CoreMemoSample」を選択します。
今度はどのEntityのファイルを作成するかを指定します。1つしかないので「Memo」を選択して次に進みます。
すると、CoreDataのクラス専用のファイルが2つ作成されます。特別な処理を追加しなければ、あとはこのまま変更しないで使えます。
ここまでの設定で、間違えた!という部分があったら、この作業で作成されるファイルを削除してからもう一度作り直せば大丈夫ですよ。
テキストフィールドとボタンの配置
CoreDataの設定が終わったら、画面の設計に入ります。今回は簡単にメモの入力と、内容を保存、削除ができるシンプルな作りにしたいと思います。
ストーリーボード上にテキストフィールドと、ボタンを2つ配置してください。テキストフィールドは文字を入力するときにキーボードが表示されるので、隠れてしまわないように上の方に作りましょう。
また、2つ作成したボタンには表示される文字も設定しておきましょう。画面右上にある「Attributes Inspector」を選択して、Titleの下の項目に”保存”、”削除”とそれぞれ入力してください。
テキストフィールドがキーボードに隠れないようにしたい方は以下の記事を参考にしてみてください。
テキストフィールドがキーボードに隠れないようにする方法【Swift低空飛行ガイド】
コーディングします
CoreDataの設定が完了したところで、データの登録や取り出しができるようにプログラムを書いていきます。変更の対象はViewController.swiftのみになります。まずはCoreDataを使えるようにimportを設定します。すでに一番上に”import UIKit”という行があるので、その下に以下の行を追加します。
import CoreData
次にCoreDataに関する情報を設定するため、以下の2行をviewDidLoad上あたりに追加しましょう。Entityの名前と項目の名前はさきほど作成したものになります。
let ENTITY_NAME = "Memo" let ITEM_NAME = "text"
次はCoreDataを扱うためのファンクションを追加します。こっちはviewDidLoadの下にでも。Swift2.0からはdo〜try〜catchの書き方が必須になりました。やるなら初めから採用してくれればいいのに。
内容は大したことしてないのですが、読み込み、更新、削除はやってることはほぼ一緒です。まず検索してデータが存在するかをチェックして、あったらそのデータを読み込むか、更新するか、削除するか処理します。新規登録とデータの読み込み、これらができればなんでもできるってことですね。
// データ登録/更新 func writeData(txtMemo: String) -> Bool{ var ret = false let appDelegate: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate) let context: NSManagedObjectContext = appDelegate.managedObjectContext let request = NSFetchRequest(entityName: ENTITY_NAME) request.returnsObjectsAsFaults = false do { let results: Array = try context.executeFetchRequest(request) if (results.count > 0 ) { // 検索して見つかったらアップデートする let obj = results[0] as! NSManagedObject let txt = obj.valueForKey(ITEM_NAME) as! String obj.setValue(txtMemo, forKey: ITEM_NAME) print("UPDATE \(txt) TO \(txtMemo)") appDelegate.saveContext() ret = true }else{ // 見つからなかったら新規登録 let entity: NSEntityDescription! = NSEntityDescription.entityForName(ENTITY_NAME, inManagedObjectContext: context) let obj = Memo(entity: entity, insertIntoManagedObjectContext: context) obj.setValue(txtMemo, forKey: ITEM_NAME) print("INSERT \(txtMemo)") do { try context.save() } catch let error as NSError { // エラー処理 print("INSERT ERROR:\(error.localizedDescription)") } ret = true } } catch let error as NSError { // エラー処理 print("FETCH ERROR:\(error.localizedDescription)") } return ret } // データ読み込み func readData() -> String{ var ret = "" let appDelegate: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate) let context: NSManagedObjectContext = appDelegate.managedObjectContext let request = NSFetchRequest(entityName: ENTITY_NAME) request.returnsObjectsAsFaults = false do { let results : Array = try context.executeFetchRequest(request) if (results.count > 0 ) { // 見つかったら読み込み let obj = results[0] as! NSManagedObject let txt = obj.valueForKey(ITEM_NAME) as! String print("READ:\(txt)") ret = txt } } catch let error as NSError { // エラー処理 print("READ ERROR:\(error.localizedDescription)") } return ret } // データ削除 func deleteData() -> Bool { var ret = false let appDelegate: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate) let context: NSManagedObjectContext = appDelegate.managedObjectContext let request = NSFetchRequest(entityName: ENTITY_NAME) request.returnsObjectsAsFaults = false do { let results : Array = try context.executeFetchRequest(request) if (results.count > 0 ) { // 見つかったら削除 let obj = results[0] as! NSManagedObject let txt = obj.valueForKey(ITEM_NAME) as! String print("DELETE \(txt)") context.deleteObject(obj) appDelegate.saveContext() } ret = true } catch let error as NSError { // エラー処理 print("FETCH ERROR:\(error.localizedDescription)") } return ret }
テキストフィールドのアウトレット接続とボタンのアクション接続
今度はストーリーボード上に作成したテキストフィールドとボタンを、プログラム側から扱えるように接続します。テキストフィールドはデータを表示させるためにアウトレット接続します。名称は”txtCoredata”にしましょう。
アウトレット接続作成の詳細は以下をご参照ください。
残りのボタン2つはボタンが押されるというアクションが発生した時に呼び出されるよう、アクション接続にします。名前はそれぞれ”pressSaveButton”、”pressDeleteButton”にしておきましょう。
起動時とボタンが押された時の処理を追加
これで最後の工程です。アプリ起動時に自動的にCoreDataにあるデータを読み込む処理と、保存ボタンが押された時の保存処理、削除ボタンが押された時にCoreDataの登録内容を削除する処理を記述します。
まずはviewDidLoadの中身から。起動時にCoreDataに登録されている内容をテキストフィールドに表示する処理を足してあげます。
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. txtCoredata.text = readData() }
次は保存ボタンが押された時。こちらはテキストフィールドに入力されているデータをCoreDataに登録する処理を追加します。これも1行だけ。
@IBAction func pressSaveButton(sender: UIButton) { writeData(txtCoredata.text!) }
最後は削除ボタンが押された時です。 CoreDataの内容を削除する処理を追加です。ついでにテキストフィールドの内容もクリアしてあげたいので、2行になりました。
@IBAction func pressDeleteButton(sender: UIButton) { txtCoredata.text = nil deleteData() }
実行します
ここまでできたらあとは実行するのみです!テキストフィールドに何か入力して保存ができます。保存した内容はアプリを再度起動したあとも残っていると思います。
CoreDataに項目を追加したり、データ構造を変更したあとに実行するとアプリが異常終了する場合があります。そんなときはシミュレーター上に作成されているアイコンを長押しして削除してから再実行してみてください。
今回のサンプルはこちらにあります。
おわりに
今回はCoreDataを扱う上での簡単なサンプルを作成しました。他にも検索条件など便利に使うには必要になる知識が必要になるので、またいつかまとめたいと思います。MagicalRecordというライブラリを使った方法はこちらで紹介しています。
[amazonjs asin=”4797389818″ locale=”JP” title=”絶対に挫折しない iPhoneアプリ開発「超」入門 増補改訂第5版 【Swift 3 & iOS 10.1以降】 完全対応 (Informatics&IDEA)”]
コメント