寄付窓口はこちら

Core Dataを守るために | try! Swift Tokyo 2019 2-15

この数年、Core Dataはローカルでの永続化において、Realmのような他のサービスを好む開発者の間ではかなりひどい評判です。このトークでは、Core Dataを使うことはそんなに悪いことではないと考える理由を説明します。Core Dataでうまくいかないところや、その落とし穴を回避する例をお見せします。また、Core Dataのアプリへの設定についても簡単に説明します。終わりには、みなさんはCore Dataをアプリに安全に統合するのに役立つ簡単なルールを用意することになるでしょう。

f:id:niwatako:20190322170803j:plain

今日話す理由ですがCoreDataが好きと人に言うと、なぜ、と言われる、ひどいじゃないか、と言われる。 でもそうではないと思っているのでその理由について説明していきたいと思います。

10年前はたしかにひどかったと思います。

新人のデベロッパの人たちは、複雑、Realmが簡単と聞いてRealmに傾倒する人がいるようですが、そうではない場合もあります。

誰にでも使える良いものだと説得するのが目的ではありません。スタックをどう設定するかをお見せし、データモデルの更新まで、この段階まで行けば状況が見えてくる。そして複数の管理オブジェクトを使うところまで見ていく。

そして最後にパフォーマンス改善まで見ていきます。

f:id:niwatako:20190322170953j:plain

使ったことがある人、たくさんコードが出ると思いました?いまは実はこんなに少ないコードでできるんですよ

f:id:niwatako:20190322171015j:plain

最初の設定にはこれしかいりません。

モデルエディタを使います、ここには新しい話はありません。

f:id:niwatako:20190322171054j:plain

f:id:niwatako:20190322171107j:plain

Attributeの名前などを設定する

Relationsihp Inspecta

f:id:niwatako:20190322171132j:plain

一つ面白い、クールなフィーチャーがあるので、砂金モデルエディタに追加されたものです。

モデル定義ではダメでサブクラス定義が必要だったと思います。モデルや設定を2箇所で定義するのは良くないと思うが、

エンティティみていただくとコード生成ドロップダウンがある

f:id:niwatako:20190322171229j:plain

自分でサブクラスを作らなくても対応してくれる。

気をつけていただきたいのはこのファイルをモディファイしないでください、アプリをビルドするたびにモディファイされてしまいます。

これをアプリにしていきましょう

f:id:niwatako:20190322171309j:plain

ほとんどのアプリでは、DBからデータをフェッチすることが必要

fetched results controllerを使う

DataStoreと同期が取れるようにしてくれる

2つほど必要なことがある。fetchリクエスト、何をとってくるべきかプロパティを指定するところ

f:id:niwatako:20190322171420j:plain

コアデータが必要なファイルだけをフェッチするようにする。

これが終わればfetched result controller をつくる

f:id:niwatako:20190322171447j:plain

2つ目のパラメータはマネージドオブジェクト、ViewContextというものがある、あとで説明する。

3つ目パラメータはTableVeiwのときに使いでがある

fetched resultを分割してくれる。

データが変わればデリゲーションに対して通知をする

f:id:niwatako:20190322171554j:plain

f:id:niwatako:20190322171620j:plain

データに変更があればcontrollerがそれを把握し、TableViewを更新する。

魔法のような流れになっています。

Delegateの流れは魔法ではなくて機能するように作らなくてはいけません。

willChangeとdidChange

f:id:niwatako:20190322171711j:plain

すべてのアップデートはデリゲート側に渡される

tableViewのUpdateに適している

f:id:niwatako:20190322171726j:plain

もう一つ、変更があったオブジェクトに対してIndexPathとともに呼び出しがかかる。

ここまで素晴らしいですよね

f:id:niwatako:20190322171843j:plain

Datamodelを少しアップデートしたい時があると思う。どうしたら良いでしょう

マイグレーションが必要でしたよね

f:id:niwatako:20190322171921j:plain

いままで新しいモデルバージョン作ったりしていましたね

最新ではどうするのでしょう

新しいプロパティを足すと自動でマイグレーション

f:id:niwatako:20190322172005j:plain

ただし単純な変更に対してのみ機能します。 追加とか削除とか。

オプショナルにとかはモデルバージョンが必要

テストはしてください。

f:id:niwatako:20190322172040j:plain

ここまでは簡単だったと思います。

では複雑になったら?

マルチコンテキスト設定

ViewContext、いくつかのオブジェクトが入っています。

一つ一つのオブジェクトはDBから取得する

ViewContextの良さは、メインスレッドの作業で対応できる、UIに載せられる。

欠点は、なにかFetchするときは大量の場合、UIがブロックされる。

バックグラウンドコンテキストを使ったほうが良い。

ViewContextにはfetched Objectができる。BackgroundContextには

f:id:niwatako:20190322172212j:plain

f:id:niwatako:20190322172216j:plain

Persistent Contextに入れるとViewContextをアップデートしてくれる

f:id:niwatako:20190322172227j:plain

f:id:niwatako:20190322172301j:plain

最初の手法はバックグラウンドコンテキストがよく使われるなら。シンプルで早いなら後者で

もう一つ見方を変えましょう。

バックグラウンドコンテキストを使った場合

Parsistent Containerにためます。ViewContextに反映される

f:id:niwatako:20190322172351j:plain

これがマルチコンテキストセットアップの作り方です。複雑に見える、難しかったが、簡素化されてきた。 オーバーヘッドが少なくなってきた。

でもよく聞く、批判、パフォーマンス

f:id:niwatako:20190322172424j:plain

CoreDataはデータベースです。

スローダウンしてしまう。

うまくいく方法を示していきたい。

まずは分析を行う。Instrumentをつかっていく、デバッグを行っていく。

InstrumentでFetchリクエストを最適化する。

このカンファレンスを例に見ていく。

Instrumentのアップデートです。Fetchリクエストをコアデータに送るとできるだけ小さな処理として実行しようとする。

キャッシュからFetch、キャッシュミスがあればDatabaseから取ってくる

f:id:niwatako:20190322172606j:plain

リストの一つのアイテム、3つのDBのクエリが切り分けられて走っている。非常に大きなものなら大変。

どう対応するか、CoreDataはRelationshipが交差するのはよく分かっている。

Prefetchするとどうか

f:id:niwatako:20190322172655j:plain

デバッガフラグを有効にする

f:id:niwatako:20190322172713j:plain

CoreDataに関するログがでる

f:id:niwatako:20190322172725j:plain

4つのクエリが実行されている。

これ以上の情報が欲しい場合

f:id:niwatako:20190322172758j:plain

1-4の数値を渡してレベルを変えられる

より多くのアウトプットが得られる

f:id:niwatako:20190322172919j:plain

結論としては、CoreDataをアプリの中でスタートするのは非常にシンプル。モデルのアップデートも簡単。マルチプルマネージドオブジェクト、これも比較的簡単に出来るようになった。パフォーマンスは非常に良くなる。ロギング、測定で改良を続けてください。