CoreDataと比較してRealmを使ったまとめ~ コメント機能付き記録アプリサンプルで見る実装のちがい ~
自己紹介
✦ 今までの仕事履歴(本業)
石川県金沢市生まれ
本業はサーバーサイドのプログラマ ※Rails&PHP使い
26歳~31歳:Ruby / PHPのプログラマ 23歳~25歳:Webデザイナー兼ディレクター
Objective-C歴:7ヶ月くらい Swift歴:半年弱
趣味:シルバーアクセサリー集め・スイーツ作り・開発
女子向け・グルメ・エンタメ関連のお仕事が多い&最近はRailsやLaravelも
Qiita : http://qiita.com/fumiyasac@githubGithub : https://github.com/fumiyasac
✦ 酒井文也(さかい ふみや)
東京(大塚)住まいの31歳
こんな格好してますが… 遊び人ではなくエンジニアです
フリーランスっぽいですが… 会社からお給料もらっているマンです
経験豊富ではありません ちゃんとした実務経験は3weekです
※心の叫び❶
※心の叫び❷
※心の叫び❸
これまでつくったもの(2015年)① 簡易家計簿アプリ「Coffre」
② ゲームアプリ「10秒虫食い算」
・カレンダーを自作しています・シンプルなお小遣い帳感覚で支出管理できます
・全問正解者ほとんどいません…
・不定期ですがコラムも書いています・サーバーサイドはRuby on Railsを使用
http://www.coffre.me/
・デザインにもこだわってみました(特にグラフ)
・実はちょっとバグがあります。・問題は今後追加予定(現在110問収録)
個人的にはなりますが、他にもアプリ・Webサービスなど開発中です(2016年も宜しくお願いします)
・サイト等は次回のアップデートで公開予定
http://blog.just1factory.net/services/284・若干の中毒性を含みます
サンプル制作でRealmを試してみて
(ぼくはこう思った) 特に上記に挙げたの3つのポイントは、サーバーサイドの開発経験があればデジャヴ感はあるはず…
✦ 今までCoreDataを使っていたけど試してみたら便利だったよ!
① CocoaPods経由のRealmSwiftのインストールがとても手軽・CocoaPodsだけではなく、手動やCarthageでインストールすることも可能
② PHPやRubyでつくられたフレームワークの書き方に近い
・StackOverflowやサポート専用Slackもあるので気軽に聞ける安心感は大きい
・特に感じたのは、Ruby on Railsの「ActiveRecord」やCakePHPやLaravelなどのORMに近い書き 方やModelクラスの考え方が個人的に馴染みやすかった(メソッドチェーンだって可能)・ViewControllerに記載する処理をシンプルにしやすい
③ 充実した公式ドキュメントやQiitaでのノウハウの多さ・実装例や書き方・メソッドの解説等が本当に豊富(CoreDataのドキュメントはちょっと読みにくい)※そんな素敵な公式ドキュメント:https://realm.io/jp/docs/swift/latest/ → この公式ドキュメントとQiitaで公開されているノウハウを元に今回のサンプルを作りました!
今回のサンプルのポイント(1)
(ぼくはこう思った) RealmとCoreDaraを比べるには同じ振る舞いの処理を比べようと思った ※Realmだけのブランチもあり
✦ RealmとCoreDataで処理は全く同じことをしているRealm選択時 CoreData選択時
❶登録されているDBの切り替えが セグメントコントロールでできる
※Realmのみのものも用意
❷UISearchBarで検索したい 文字列を入れるとタイトル or 詳細内容で
部分一致検索ができる
❸評価の高い順 or 新しい投稿順 で内容がソートできる
今回のサンプルのポイント(2)
(ぼくはこう思った) CoreDataのアソシエーションは癖がかなりあるため、今回はあえて使用せずに[1:n]の対応をしました
✦ 1件食べ物&おみやげデータに紐づくn件のコメント機能コメント一覧 データの追加部分
❶まずは食べ物&おみやげデータを 登録する。そしたら評価とコメントをn件
追加できるようになる
❷食べ物&おみやげデータ及び それに紐づくコメントを画像付きで
追加することができる
❸コメントを登録したタイミング で評価の平均値を算出する
実現のための構成と設計
(ぼくはこう思った) いきなりモデルを作成するのではなく、テーブル定義や初期値に関してはしっかりと定義をしておくと良い
✦ RealmにおけるDB設計をする際のポイント
・プライマリキーやテーブル同士の関連付け、初期値の設定はModelクラスで行う
Omiyage.id
・ビジネスロジックに相当するような処理に関してはできるだけModelクラスに記述する
1 n OmiyageComment.omiyage_id ※今回は手動での関連付け
・画像データ(UIImage型)はRealmに登録する際はNSData?型に変換する必要がある
サンプルとデモの紹介
(ぼくはこう思った) RealmでのDB定義やメソッド等の解説は後述しますが実装もかなり見通しが良くなっている気がします
✦ 実際のサンプルで動きを確認しながら見ていきましょう!① Githubhttps://github.com/fumiyasac/DatabasePersistencePattern
② Qiita・Realmを使用したコメント機能付き記録アプリで見る実装ポイント(CoreDataと比較付き) http://qiita.com/fumiyasac@github/items/cbdf4d36cc14e213aaa7 → まだまだ未完全な部分もあるのでサンプル共々今後修正&追記予定。
③ 解説のポイント・サンプルはCoreDataとRealmで同一のデータの振る舞いをするのでその部分をチェック・Model側のメソッドのざっくりとした定義と実装方法を知ろう
デモりますので画面に注目!④ 実際の挙動や操作に関して
CoreDataと比較した際に✦ 個人的な雑感も含むこれはいいと感じた点
PrimaryKeyやIndexに関する設定の考慮
ControllerとModelの明確な分離
・CoreDataは仕様上プライマリキーへのアクセスができなかった → このためテーブル定義や関連付けが複雑になってしまう場合には注意が必要 → UUIDや(最大ID + 1)の値を使って「ダミーの一意な値」を作成するケースが多かった・Realmではプライマリキーやインデックス等のRDBにもあるような設定が可能
・CoreDataのサンプルではControllerにデータ処理を記述しているものが多い(ViewModelを作る等は可) → RealmのサンプルではModelクラスに明確に分離して記述されているものが多い
・画像データ保存時の型変換の必要性やトランザクションのサポート ・最大値 / 最小値 / 平均 / 合計といった集計に関するものも用意されている
(ぼくはこう思った) RealmとCoreDataを比較した際に違いはもちろん、CoreDataとの共通点にも着目してみると良いです
RealmでもCoreDataと共通点がある主な事項
サンプルであり得るデータの振る舞い✦ 今回のサンプルであり得るRealmでの書き方のケーススタディ(1)
Omiyageテーブル及びOmiyageCommentテーブルへデータを1件新規登録
※OmiyageCommentに登録するケースで解説
//Realmにデータを1件登録する let omiyageCommentObject = OmiyageComment.create() omiyageCommentObject.comment = self.omiyageCommentDetail omiyageCommentObject.star = self.omiyageCommentStar omiyageCommentObject.image = self.omiyageCommentImage omiyageCommentObject.omiyage_id = self.detailId //登録処理 omiyageCommentObject.save()
・create()メソッドでPrimaryKeyと各プロパティの初期値が入る ・各プロパティに値をセットしていく ・save()メソッドで新規追加を実行
(ぼくはこう思った) 書き方の部分がちょっとなんか見覚えのある感じですね。各Modelクラスを確認しておきましょう
・ざっくりと解説
create()メソッドでインスタンス 作成タイミングでPrimaryKeyが
付与されるます。 ※詳細はOmiyageComment.swift
サンプルであり得るデータの振る舞い✦ 今回のサンプルであり得るRealmでの書き方のケーススタディ(2)
(ぼくはこう思った) 集計に関する処理の方法やfilter()メソッドでの検索条件の記述方法をしっかりと押さえておきましょう
OmiyageCommentを1件新規登録後にOmiyageデータの評価平均値を更新
//食べもの&おみやげデータの評価平均値の算出 [AddComment.swift] let average = OmiyageComment.getAverage(1) Omiyage.updateAverage(average, target_id: 1)
//食べもの&おみやげデータIDに紐づくの評価平均値の計算 [OmiyageComment.swift] realm.objects(OmiyageComment).filter("omiyage_id = %@", 1).average(“star")
//食べもの&おみやげデータIDに紐づくの評価平均値の更新 [Omiyage.swift] let omiyages = realm.objects(Omiyage).filter("id = %@", 1) try! realm.write { omiyages.setValue(average, forKey: "average") }
・filter()メソッドで条件を指定する ※今回はomiyage_idでの絞り込み ・RealmSwiftで更新処理を行う場合はwrite {…} 内にsetValueで更新したい値を入れる
・ざっくりと解説
fliter()メソッドでの条件指定の方法 に関しては公式ドキュメントをCheck!
サンプルであり得るデータの振る舞い✦ 今回のサンプルであり得るRealmでの書き方のケーススタディ(3)
(ぼくはこう思った) NSPredicateやsorted()・メソッドチェーン等を使用して複雑なクエリに該当する処理も記述できる!
Omiyageデータのソーティングと検索キーワードとの部分一致
//食べもの&おみやげデータのソーティングと検索キーワードに一致するデータの取得 [Omiyage.swift] //※今回の例では「検索キーワード:牛丼」、「ソーティング:平均評価」、「順番:大きい順」を想定 var omiyages: Results<Omiyage> let predicate = NSPredicate(format: "title CONTAINS %@ OR detail CONTAINS %@", “牛丼”, “牛丼”) omiyages = realm.objects(Omiyage).sorted("average", ascending: false).filter(predicate)
・NSPredicate型のインスタンスで検索条件を設定 ・CONTAINS %@ で該当カラムに牛丼が含まれているかを見る ・sorted(“average”, ascending: false)では、 第1引数:ソート対象のカラム、第2引数:大きい順 or 小さい順の指定
・ざっくりと解説fliter()メソッドでの条件は NSPredicateを使うと
複雑な場合何かと便利です。
・SQLでのイメージSELECT * FROM omiyage WHERE (title LIKE '%牛丼%' OR detail LIKE '%牛丼%') ORDER BY average DESC;
今後自分のアプリ開発においても積極的に活用してどんどん実際のアプリやショーケースを増やしたい!
✦ RealmでのModelの記載は他のフレームワークのORMとも似ている
セッションのまとめ
✦ ControllerとModelを明確に分けてFatController防止にもなる
✦ CoreDataで使用できるような集計関数等やトランザクションにも対応しているので意外と共通点もある
✦ 複雑なクエリになるような処理でも割とシンプル&直感的に記述可能
✦ 公式ドキュメントの充実さはもちろんSlack上でのオンラインでのサポート等の体制が整っているのはとてもありがたい!
ご静聴ありがとうございました!
✦ PrimaryKeyやインデックス等RDBでもよくある設定もModelにできる