【Swift】Core Dataの使い方。保存データの更新、削除、ロールバックを行う。(Swift 2.1、XCode 7.2)
保存データの更新、削除、ロールバック
前回の記事までは、Core Dataを使って外部ファイルに新しいデータを追加する実装方法を説明してきた。本記事では、外部ファイルに保存したデータを更新、削除したり、外部ファイルに保存する前に取り消す実装方法について説明する。
以降の手順を行う前のXcodeプロジェクトをGitHubに置いたので、試してみる人はご利用されたし。
⇒「テスト用プロジェクト」
事前準備では、Search Barに入力した文字列を含むデータを一覧表示するようにしておいた。「取り消しボタン」と「保存ボタン」の機能はまだ実装していない。
以下は変更前のViewController.swiefのソースコード
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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
// // ViewController.swift // import UIKit import CoreData class ViewController: UIViewController, UITableViewDataSource, UISearchBarDelegate, UITableViewDelegate{ @IBOutlet weak var testTableView: UITableView! @IBOutlet weak var testSearchBar: UISearchBar! //管理オブジェクトコンテキスト var managedContext:NSManagedObjectContext! //検証用データ let dataList = [["月刊コロコロコミック", "小学館",390,20.2,"2016/5/16 10:30:00"], ["コロコロイチバン!","小学館",540,25.3,"2016/4/23 09:00:00"], ["最強ジャンプ","集英社",420,13.2,"2016/6/9 7:00:00"], ["Vジャンプ","集英社",300,13.4,"2016/1/3 12:00:00"], ["週刊少年サンデー","小学館",280,16.7,"2016/8/23 11:00:00"], ["週刊少年マガジン","講談社",250,40.5,"2016/10/10 7:30:00"], ["週刊少年ジャンプ","集英社",300,60.3,"2016/9/9 10:00:00"], ["週刊少年チャンピオン","秋田書店",280,23.5,"2015/5/1 11:30:00"], ["月刊少年マガジン","講談社",320,45.1,"2016/7/2 13:30:00"], ["月刊少年チャンピオン","秋田書店",220,12.6,"2015/11/10 7:30:00"], ["月刊少年ガンガン","スクウェア",240,33.5,"2016/2/2 7:30:00"], ["月刊少年エース","KADOKAWA", 330,9.8,"2016/7/1 8:30:00"], ["月刊少年シリウス","講談社",350,20.2,"2016/11/26 15:00:00"], ["週刊ヤングジャンプ","集英社",300,33.3,"2014/3/16 8:30:00"], ["ビッグコミックスピリッツ","小学館",240,11.2,"2014/9/29 11:30:00"], ["週刊ヤングマガジン","講談社",310,26.7,"2016/8/8 10:00:00"]] //検索結果配列 var searchResult = [Book]() //本を保存するメソッド func insertBook(){ do { //Bookエンティティの件数を取得する。。 let fetchRequest = NSFetchRequest(entityName: "Book") fetchRequest.resultType = .CountResultType let result = try managedContext.executeFetchRequest(fetchRequest) as! [Int] if(result[0] == 0){ //何も保存されていない場合は検証用データを保存する。 for data in dataList { let book = NSEntityDescription.insertNewObjectForEntityForName("Book", inManagedObjectContext: managedContext) as! Book book.name = data[0] as? String //雑誌名 book.publisher = data[1] as? String //出版社 book.price = data[2] as? Int //価格 book.approvalRate = data[3] as? Float //支持率 let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "yyyy/M/d H:mm:ss" book.releaseDate = dateFormatter.dateFromString(data[4] as! String)! //発売日 } //管理オブジェクトコンテキストの中身を保存する。 try managedContext.save() } } catch { print(error) } } //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() //デリゲート先を自分に設定する。 testSearchBar.delegate = self //何も入力されていなくてもReturnキーを押せるようにする。 testSearchBar.enablesReturnKeyAutomatically = false //管理オブジェクトコンテキストを取得する。 let applicationDelegate = UIApplication.sharedApplication().delegate as! AppDelegate managedContext = applicationDelegate.managedObjectContext //コンフリクトが発生した場合はマージする。 managedContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy //検証用データを格納する。 insertBook() //デリゲート先に自分を設定する。 testTableView.delegate = self } //データを返すメソッド func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell { //セルを取得する。 let cell = tableView.dequeueReusableCellWithIdentifier("TestCell", forIndexPath:indexPath) as UITableViewCell //セルのラベルに本のタイトルを設定する。 let book = searchResult[indexPath.row] cell.textLabel?.text = "\(book.name!) \(book.price!)円" return cell } //データの個数を返すメソッド func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int { return searchResult.count } //検索ボタン押下時の呼び出しメソッド func searchBarSearchButtonClicked(searchBar: UISearchBar) { //キーボードをしまう。 testSearchBar.endEditing(true) //検索結果配列を作る。 makeSearchResult() //テーブルを再読み込みする。 testTableView.reloadData() } //検索結果配列作成メソッド func makeSearchResult() { //検索結果配列を空にする。 searchResult.removeAll() //フェッチリクエストのインスタンスを生成する。 let fetchRequest = NSFetchRequest(entityName: "Book") if(testSearchBar.text != "") { //属性nameが検索文字列を含むデータをフェッチ対象にする。 fetchRequest.predicate = NSPredicate(format:"name CONTAINS %@", testSearchBar.text!) } do { //フェッチリクエストを実行する。 searchResult = try managedContext.executeFetchRequest(fetchRequest) as! [Book] } catch { print(error) } } } |
保存データの取得と更新
セルをタップすると雑誌の価格が100円増加するようにする。
UIViewController.swiftに以下のメソッドを追加する。
選択行の価格表示を更新したあと、管理オブジェクトコンテキストからオブジェクトを取得し、priceの値を更新している。この段階ではまだ外部ファイルに保存されていない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//セル選択時の呼び出しメソッド func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){ do { //検索結果配列を更新する。 let book = searchResult[indexPath.row] book.price = Int(book.price!) + 100 //テーブルの選択行を再読み込みする。 testTableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade) //選択行のオブジェクトを管理オブジェクトコンテキストから取得する。 let fetchRequest = NSFetchRequest(entityName: "Book") fetchRequest.predicate = NSPredicate(format:"name = %@", searchResult[indexPath.row].name!) if let result = try managedContext.executeFetchRequest(fetchRequest) as? [Book] { //オブジェクトのpriceを更新する。 result[0].price = book.price } } catch { print(error) } } |
コミット、ロールバック
ViewController.swiftに以下のメソッドを追加する。
「保存ボタン」では、管理オブジェクトコンテキストのsaveメソッドを呼び出して外部ファイルにデータを保存している。
「取り消しボタン」では、rollbackメソッドを呼び出して管理オブジェクトコンテキストに格納されているオブジェクトを破棄している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//保存ボタン押下時の呼び出しメソッド @IBAction func pushSaveButton(sender: UIButton) { do { //管理オブジェクトコンテキストの中身を保存する。 try managedContext.save() } catch { print(error) } } //取り消しボタン押下時の呼び出しメソッド @IBAction func pushCancelButton(sender: UIButton) { //管理オブジェクトコンテキストの中身を保存する。 managedContext.rollback() //検索結果配列を作成する。 makeSearchResult() //テーブルを再読み込みする。 testTableView.reloadData() } |
以下は、この時点で実行したプレイ動画。保存ボタンで変更確定、取り消しボタンで表示が元に戻る。
データの削除
次に、セルをスワイプして削除する機能を実装する。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 |
//編集可否を答えるメソッド func tableView(tableView: UITableView,canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { //すべての行を編集可能にする。 return true } //テーブルビュー編集時の呼び出しメソッド func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { if editingStyle == UITableViewCellEditingStyle.Delete { do { //選択行のオブジェクトを管理オブジェクトコンテキストから取得する。 let fetchRequest = NSFetchRequest(entityName: "Book") fetchRequest.predicate = NSPredicate(format:"name = %@", searchResult[indexPath.row].name!) if let result = try managedContext.executeFetchRequest(fetchRequest) as? [Book] { //検索結果配列から選択行のオブジェクトを削除する。 searchResult.removeAtIndex(indexPath.row) //テーブルビューから選択行を削除する。 tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade) //管理オブジェクトコンテキストから選択行のオブジェクトを削除する。 managedContext.deleteObject(result[0]) } } catch { print(error) } } } |
以下は実際のプレイ動画