いよいよRealm活用編!
値を保存する方法をマスターしましょう!
1. モデルファイルを作成
Realmデータベースを使うために、まずはモデルを作成しましょう。
新規ファイルを追加するときにいつも行っている「New File...」を選択します。
Realm Pluginが正しく導入されていれば、下にスクロールすると「Realm Model Object」が表示されています。選択して次へ。
Model Object Classには、「Diary」と入力。
2. モデルファイルの中身を考える
いま、Objectクラスを継承した「Diary」クラスを作成しました。今度はこの中身を考えていきましょう。
いま、データベースの枠を作っているものと思ってください。
日記アプリにはどの要素が必要でしょうか?
今回は、以下のようなデータベースを作成することを目標にしていきます。
※どうでもいいですが、筆者の実話です。
そこで、今回は一番上の部分を考えていきます。
もし要素がほかにもあるようなら、自分で追加してかんがえてみてください。
このデータベースの中身ではなく枠の部分(今回は青い背景の部分)を「エンティティ」といいます。
データベース設計を考えるときにはエンティティの設計がとても大事です。
今回は「日付」「本文」「写真」と3つのエンティティがありますが、それぞれのエンティティに名前と型をつけていきましょう。
それでは、上図のとおりにDiary.swift
にモデルを定義していきます。
上図のような表はデータベースを設計するときはかならず作りましょう!
Google Spreadsheetや、Excelのような表で構いません。あとで自分がコードを見たときに思い出しやすくなります。
//
// Diary.swift
// DiarySampler
//
// Created by Ryo Eguchi on 2017/01/02.
// Copyright © 2017年 Ryo Eguchi. All rights reserved.
//
import Foundation
import RealmSwift
class Diary: Object {
dynamic var date = ""
dynamic var context = ""
dynamic var photo: NSData? = nil
override static func primaryKey() -> String? {
return "date"
}
}
date
をPrimary Keyとして設定しました。Primary Key(以下、「PK」)として設定することで、一意性を保ちます。(重複してはいけないという意。)
※画像はその4で扱います。
それぞれのエンティティの作成方法は、チートシートを参考にしてください。
3. 日記作成画面の設置
関連付けもわすれずにしておきましょう。
@IBOutlet var writeButton: UIButton!
// (中略)
@IBAction func writeButtonPushed(_ sender: UIButton) {
}
続いて、2番目のViewController(DiaryViewController)についても同様に関連付けします。
あらかじめNew File...で新しくViewControllerファイルを作っておきましょう。
//
// DiaryViewController.swift
// DiarySampler
//
// Created by Ryo Eguchi on 2017/01/02.
// Copyright © 2017年 Ryo Eguchi. All rights reserved.
//
import UIKit
//このファイル内でRealmを使うのでここを追加
import RealmSwift
class DiaryViewController: UIViewController {
@IBOutlet var dateLabel: UILabel!
@IBOutlet var contextTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func saveButtonPushed(_ sender: UIButton) {
}
}
4. 情報をDiaryViewControllerに集約させよう
つまり、すべての情報が「DiaryViewController」に集約され、そこから情報がRealm Databaseに格納されます。
そこで、情報をすべてDiaryViewControllerに集約させましょう。
4-1. dateの受け皿を準備する
DiaryViewControllerに、dateの受け皿を準備しましょう。
変数を宣言します。
var date: String!
実際にラベルに表示させるようにしておきましょう。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
dateLabel.text = date
}
4-2. 受け渡し元のViewControllerで処理を記述
var date: String!
// (中略)
func didSelectDay(_ dayView: JBDatePickerDayView) {
print("date selected: \(dateFormatter.string(from: dayView.date!))")
date = dateFormatter.string(from: dayView.date!) //追加
}
@IBAction func writeButtonPushed(_ sender: UIButton) {
self.performSegue(withIdentifier: "toDiary", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "toDiary") {
let diaryView = segue.destination as! DiaryViewController
diaryView.date = self.date
}
}
まずはじめに、dateをString型で宣言しておきます。
1つめのメソッド「didSelectDay」は、JBDatePickerDayViewのDelegateメソッドです。
dateにstring型で日付を挿入しています。
2つめのメソッド「writeButtonPushed」は、「書く」ボタンをおした時のメソッドで、画面遷移のコードを書いています。
3つめのメソッド「prepare for segue」は、画面遷移するときに必ず呼ばれるメソッドです。(知らなかったら覚えておこう!)
今回は、「toDiary」の画面遷移をするときに、遷移先のdateという変数に遷移元の変数dateの値を代入します。これで、画面遷移したときに遷移先のdate変数に初期値がセットされました。
4-3. Storyboardで画面をつなげる
Storyboardで1つめの画面と2つめの画面をつなげておきましょう。
Identifierは「toDiary」と記述します。
これで、保存はされませんが、シミュレータで実行して画面遷移ができることを確認しましょう。
ただしく実行できれば、上図のように画面上部のラベルに日付が表示されます。
5. Realmに保存
最後に、Realmに保存(書き込み)しましょう。
@IBAction func saveButtonPushed(_ sender: UIButton) {
// STEP.1 Realmを初期化
let realm = try! Realm()
//STEP.2 保存する要素を書く
let diary = Diary()
diary.date = date
diary.context = contextTextView.text
//STEP.3 Realmに書き込み
try! realm.write {
realm.add(diary, update: true)
}
//画面遷移して前の画面に戻る
self.dismiss(animated: true, completion: nil)
}
この地点でRealmに値を書き込んでいるので、きちんと値が書き込まれているかRealm Browserで確認してみましょう。
確認方法はこちらの記事が参考になります。
【Swift】Realm BrowserでRealm Mobile Databaseの中身を確認する
6. Realmから値を読み込む
もし、すでにRealmに値がある場合は、Realmから本文を取ってくるようにしましょう。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
dateLabel.text = date
DispatchQueue(label: "background").async {
let realm = try! Realm()
if let savedDiary = realm.objects(Diary.self).filter("date == '\(self.date!)'").last {
let context = savedDiary.context
DispatchQueue.main.async {
self.contextTextView.text = context
}
}
}
}
DispatchQueue(label:"background")
では、メインスレッドではなくバックスレッドで処理を実行しています。これをすることで、メインスレッドの処理に影響することなくバックスレッドでQueue(検索して読み込み)を行うことができます。
let savedDiary = realm.objects(Diary.self).filter("date == '\(self.date!)'").last
これは、
realm.objects(Diary.self)
ここで、Diaryオブジェクトをすべて読みこんでいます。そこに、
.filter("date == '\(self.date!)'")
filter(フィルタ)をかけてあげます。今回は、dateエンティティが「self.date!」(選択した日付)と等しいものをすべて読み込んでいます。
さらに、
.last
これで、フィルタをかけたオブジェクトのうちで一番最後(最新)のもの、という意味です。
DispatchQueue.main.async {
self.contextTextView.text = context
}
これは、DispatchQueueで処理するスレッドを変えていますが、画面表示に関わることなのでバックスレッドでは処理できません。そこで、あえてメインスレッドに変えて処理を実行しています。