Core Data Editor のプロジェクト表示を高速化する

Core Data Editor は Core Data に格納されたデータを閲覧・編集することができるオープンソースのアプリです。

github.com

Core Data を利用したアプリの開発の際に役立ちますが、プロジェクトの読み込みに非常に時間がかかります。

この記事では Core Data Editor のプロジェクト読み込みに関する問題点を調査し、処理を高速化するための修正について記述します。

問題点

現在の Core Data Editor におけるプロジェクト表示に関する問題点は以下の通りです。

1) Core Data を使用したプロジェクトを表示するまでに非常に長い時間がかかります。

Core Data Editor では Core Data を使用したプロジェクトを検出してリストに表示してくれます。この処理に非常に長い時間がかかっており、私の環境ではプロジェクトが表示されるまでに 30 秒ほどの時間を要します。

2) 同一の Core Data Model を使用しているプロジェクトが存在する場合に、プロジェクトが重複して表示されます。

プロジェクト A と プロジェクト B が共有フレームワークなどにより同一の Core Data Model を使用していた場合、Core Data Editor のプロジェクトリストにはプロジェクトが重複して表示されます。

f:id:watanabetoshinori:20180612151345p:plain

原因

Core Data Editor のソースコードを取得してプロジェクト表示に関連する処理を確認したところ、以下のようなフローが実装されていました。

  1. iOSシミュレーターのディレクトリを特定します。(~/Library/Developer/CoreSimulator/
  2. iOSシミュレーターのディレクトリに存在する全てのファイルとサブディレクトリをチェックして、Core Data の Model ファイルと SQLite ファイルを検出します。
  3. 検出した Model ファイルと SQLite ファイルを照らし合わせて、一致している場合はプロジェクトを表示対象に加えます。
  4. 表示対象のプロジェクトをリスト表示します。

プロジェクト表示までに非常に時間がかかるのは、2. の処理において全てのファイルとサブディレクトリをチェックしているためです。私の環境では3万以上のファイル・サブディレクトリがチェック対象となっていました。 iOSシミュレーターにインストールされているアプリが多い場合はチェック対象となるファイルはさらに増えるため、プロジェクト表示までの時間も増加することが予想されます。

また、プロジェクトが重複して表示されるのは、3. の処理に問題がありました。Model と SQLite ファイルが一致しているかどうかという比較により判定を行っているため、複数のプロジェクトが同じ Model を使用していた場合に誤検出されていました。

iOSシミュレーターのディレクトリ構造

これは元々の処理が若干大雑把だった感じではありますが、iOSシミュレーターのディレクトリ構造の変更も原因と思われます。iOS シミュレーターのディレクトリ構造は iOS の更新に合わせて変更が加えられてきました。

現時点での最新版である Xcode 9.4 に付属する iOS シミュレーターでは以下のようなディレクトリ構造になっています。

1) 端末

iOSシミュレーターの端末は ~/Library/Developer/CoreSimulator/Devices/ 以下に、1端末ごとに1ディレクトリが作成されています。

f:id:watanabetoshinori:20180612145353p:plain

2) プロジェクト(アプリ本体)

プロジェクトは各端末のディレクトリ内の data/Containers/Bundle/Application 以下に保存されています。

f:id:watanabetoshinori:20180612145659p:plain

3) プロジェクトのデータ(Sandbox)

プロジェクトのデータは各端末のディレクトリ内の data/Containers/Data/Application 以下に保存されています。

f:id:watanabetoshinori:20180612145714p:plain

プロジェクトとプロジェクトのデータを紐付けるための設定は .com.apple.mobile_container_manager.metadata.plist という非表示ファイルに保存されています。

f:id:watanabetoshinori:20180612152253p:plain f:id:watanabetoshinori:20180612152320p:plain

この plist には Dictionary型のデータが記述されており、その中の MCMMetadataIdentifier というキーの値がアプリの Bundle Identifier と同一の値になっています。プロジェクトの Bundle Identifier と、プロジェクトのデータの Bundle Identifier を比較することでプロジェクトを特定することができそうです。

修正の実装

iOSシミュレーターのディレクトリ構造が判明しましたので、それを踏まえてプロジェクト表示のフローを修正します。修正すべき内容は以下の2点になります。

  1. iOSシミュレーターのディレクトリに存在する全てのファイルとサブディレクトリをチェックするのではなく、プロジェクトとプロジェクトのデータのディレクトリのみをチェックするように変更します。
  2. 表示対象のプロジェクトを特定する際に、Model と SQLite ファイルの比較ではなく、Bundle Identifier を参照して特定するように変更します。

これらの修正を行った最終的なコードは以下のようになります。

https://github.com/watanabetoshinori/Core-Data-Editor/commit/18b8425fb06005445574c05f2d2f3cddea60f291

修正後のコードを試したところ、アプリがリスト表示されるまでの時間はおよそ 3 秒にまで短縮されました。 ブランチ元に Pull Reqeust は出していないので、以下のレポジトリからコードを取得して試すことができます。

https://github.com/watanabetoshinori/Core-Data-Editor