LoginSignup
23
23

More than 3 years have passed since last update.

Realmを使用した日記アプリ

Last updated at Posted at 2018-12-06

Realmとは、、、

一言で言うとデーターベース。
iOSアプリでデータを保存したい時に使用されます。
データ保存は、Realmの他にUserDefaultsやCoreDataが用いられる事もあります。

CoreDataよりも直感的に利用でき、動作も速いことが特徴。
Swiftに対応したRealmSwiftがあるため、今回は使用していきます。。

以下、ドキュメント
Realm

アプリ作成

環境

  • Xcode 10.1
  • Swift 4.2
  • Cocoapods 1.5.3

ソースコード

gitHubにあげているので、全体のコードはこちら。
shun6934/DiaryApp

参考サイト

Realmの導入

1. Cocoapodsの導入

今回は、CocoapodsでRealmを管理します。
Cocoapodsは、Rubyが必須ですが、
Macではデフォで入っているため気にしなくていいです。

以下のコマンドを叩いて、インストールします。
$ sudo gem install cocoapods

正しくインストールされていれば、
$ pod --version
でversionを確認できます。

2. プロジェクト作成

Xcodeを開き、
Create a new Xcode project -> Single View App
で作成。
image.png

アプリ名は、Diary_Appとしておきます。

3. Podfile

ターミナルを開き、先ほど作成したアプリのディレクトリまで移動した後、
$ pod init
でPodfileを作成し、
$ vi Podfile
で編集します。

今回は、データを保存したいため、データベースであるRealmをSwiftで使うためのRealmSwift
カレンダーを表示するためのJBDatePickerをライブラリーとして用います。

Podfile
# Uncomment the next line to define a global platform for your project
# platform :ios, '12.1'
target 'Diary_App' do
   # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
   use_frameworks!
   # 以下、追加
   pod 'JBDatePicker'
   pod 'RealmSwift'

   # Pods for Diary_App
end

編集できたら保存し、
$ pod install
を行ないます。

image.png

警告は出ていますが、無事にインストールされています。

これで準備できたので、これからコードを書いていきます。

カレンダー表示

1. Viewの配置

Main.storyboardを開き、
カレンダーを表示したいViewControllerにViewを配置します。
image.png

次に、先ほど配置したViewに対してJBDatePickerViewClassを定義します。
Moduleに関しては、Classを入力した際に決まるため、入力する必要はありません。
image.png

2. コードの紐付け

Storyboardに配置したViewをコードに紐付けさせる必要があります。
Viewを選択した状態で、Ctrを押しながらコード上に引っ張っていきます。
変数名は、datePickerViewとします。
image.png

これだけではエラーが出るため、モジュールをimportする必要があります。
また、Delegateプロトコルを宣言する必要があります。

ViewController.swift
import UIKit
import JBDatePicker // import

class ViewController: UIViewController, JBDatePickerViewDelegate { // Delegete宣言

    @IBOutlet weak var datePickerView: JBDatePickerView!  // カレンダー

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        datePickerView.delegate = self // DelegateをControllerに追加

    }

    // 日付選択時
    func didSelectDay(_ dayView: JBDatePickerDayView) {
        print("day selected:\(dayView.date!)")
    }!

}

ここで一旦実行してみましょう。
しっかりとカレンダーが表示されていたらokです。

また、任意の日付をタップすると
コンソールに日付が表示されると思います。

image.png

3. 日付の表示

コンソールに表示された日付は、タイムゾーンを無視しているため、
それを考慮する必要があります。

以下のコードを追加して、日本の日付にします。

    lazy var dateFormatter: DateFormatter = {
        var formatter = DateFormatter()
        formatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "ydMMM",
                                                        options: 0,
                                                        locale: Locale(identifier: "ja_JP"))
        formatter.timeZone = TimeZone(identifier: "Asia/Tokyo")
        return formatter
    }()

日付を変更できたため、didSelectDayを書き換えます。

    func didSelectDay(_ dayView: JBDatePickerDayView) {
        print("day selected:\(dateFormatter.string(from: dayView.date!))")
    }

もう一度実行すると、コンソールはこのように変化します。

day selected:2018年12月5日

データベースの構築

1. モデルの用意

File -> New -> File
で新規ファイルを作成。

image.png

CocoaTouchを選択し、名前をDiary、SubClassをObjectにしたら、
保存します。

image.png
image.png

作成した後は、Objectでエラーが出るため、
RealmSwiftをimportします。

Diary.swif
import RealmSwift

class Diary: Object {

}

2. モデルの構成

日記に必要な情報は、「日付」と「本文」です。
日付と本文は、String型です。
Primary Keyとしてdateを設定します。

Diary.swift
   @objc dynamic var date: String = ""
   @objc dynamic var context: String = ""

   open var primaryKey: String {
        return "date"
   }

データーベースのモデルができたため、日記を打ち込む画面を作成します。

日記

1. 日記画面

Main.storyboardで以下のように画面を作成します。

新しい画面の追加に伴い、ファイルも作成しておきます。
カレンダー表示と同様に、LabelやTextViewの紐付けをします。

image.png

ViewController.swift
import UIKit
import JBDatePicker

class ViewController: UIViewController, JBDatePickerViewDelegate {

    @IBOutlet weak var datePickerView: JBDatePickerView! 
    @IBOutlet weak var writeButton: UIButton! // 追加

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        datePickerView.delegate = self

    }

    func didSelectDay(_ dayView: JBDatePickerDayView) {
        print("day selected:\(dayView.date!)")
    }

    // 追加
    @IBAction func writeButtonPushed(_ sender: UIButton) {

    }

}

DiaryViewController.swift
import UIKit
import RealmSwift //ここでRealmを使う 

class DiaryViewController: UIViewController, UITextViewDelegate { //Delegate宣言

    // 紐付け
    @IBOutlet weak var dateLabel: UILabel!
    @IBOutlet weak var contextView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // TextViewの設定
        contextView.delegate = self
        contextView.layer.borderColor = UIColor.black.cgColor
        contextView.layer.borderWidth = 1.0
        contextView.layer.cornerRadius = 10.0
        contextView.layer.masksToBounds = true

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // 「保存」ボタンを押した時の処理
    @IBAction func saveButtonPushed(_ sender: UIButton) {

    }
}

2. データの受け渡し

日記画面の日付に、カレンダー画面での日付を渡さなくてはいけないため、
dateとして渡してあげます。
まず、受け渡し元のViewControllerで以下のコードを書きます。

ViewController.swift
   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
        }
    }

segueを設定したため、Storyboardでも設定します。

image.png

次に、受け渡し先であるDiaryViewControllerに受け皿を用意してあげます。
困惑しないように、変数名を統一します。

DiaryViewController.swift
   var date: String! // 受け皿の用意


   override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        dateLabel.text = date // Labelに日付を表示

    }

これで日付の受け渡しができたため、
本文に書いた内容を保存していきます。

3. Realmに保存

保存したいタイミングが「保存」ボタンが押されたときであるため、
先ほど紐付けたメソッドに書いていきます。

DiaryViewController.swift

    // 「保存」ボタンを押した時の処理
    @IBAction func saveButtonPushed(_ sender: UIButton) {
        let realm = try! Realm() // Realmの初期化

        let diary = Diary() // モデルのインスタンス化
        diary.date = date
        diary.context = contextView.text

        try! realm.write {
            realm.add(diary, update: true) // Realmに追加
        }

        self.dismiss(animated: true, completion: nil) // 前の画面に戻る
    }

値がきちんと保存されているか確認するために、
Realm Browserで確認します。

確認方法は、以下のサイト参考。
【Swift】Realm BrowserでRealm Mobile Databaseの中身を確認する

4. Realmからの値の読み込み

過去に書いた日記を表示したいため、値を読み込み、表示する必要があります。

DiaryViewController.swift
    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.contextView.text = context
                }
            }
        }

    }

完成

これでできたため、実行してみましょう。

以下になったら、完成です。

image.png

image.png

更新

この記事で一つ欠点があったので、修正します。
コメントでのご指摘ありがとうございます。

カレンダー表示時に、日付をタップせずにそのまま書き込むボタンを押してしまうと
アプリが落ちてしまいます。

原因は、date

23
23
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
23