blogレシピ動画サービス「kurashiru」を運営するdelyのテックブログ2019-10-08iOS版クラシルのフィードを滑らかな動きにするためにやったことTweetこんにちは、iOSエンジニアのtakao(takaoh717)です今回はクラシルiOSアプリのフィードのパフォーマンス改善を行った話をご紹介します。改善を行ったフィードはUICollectionViewで構成されており、レシピ、画像バナー、広告など複数の異なる型のデータを表示しているような画面です。今回行った変更は以下の内容です。改善を行う前は、アプリを動かしていると実際に分かるレベルでパフォーマンスに問題がありました。スクロール自体の挙動が若干重くてスムーズじゃない(指の動きに対して若干ひっかかりがある)ページングの読み込みをしたときにスクロールが止まることがある更新時に画面がチラつくことがある差分更新ライブラリの導入とロジックの見直しまず最初に、差分更新ライブラリの導入を行いました。これまでは、一部分のみ自前のロジックで差分更新を行って、基本的にはreloadData()を多用しているような状態でした。何度か差分更新を行うようにしたことはあるのですが、更新タイミングによるクラッシュなどが度々発生し、結局reloadData()に戻すようなことをしていました。そこで、今回のリファクタリングを気にreloadData()を使用しない状態にしておきたかったので、ライブラリを導入しました。差分更新用のライブラリはDifferenceKitを選択しました。github.com選定理由では以下の点が判断基準になりました。パフォーマンスの高さプロジェクトへの導入コスト、実装コストの低さパフォーマンスについては実際に計測して比較はしていないのですが、公式のドキュメントに記載されている内容では、RxDataSourcesやIGListKitよりも高速になっているようです。実装面については、classだけではなくstructにも対応している点や、AnyDifferentiableを使って異なる型のデータを一つの配列で管理可能な点がポイントでした。Examplesに動くアプリのコードが載っているのでそちらとドキュメントを参考にして実装しました。実装方法まず、フィードに表示するコンテンツを表すModelにDifferentiableを準拠させます。}}今回の実装ではフィードの1列を1Sectionで表現する構造にしてみました。Sectionのデータ管理をしやすいようにDataSourceは以下のような実装にしました。AnyDifferentiable>更新時の実装も変更前と後の配列を用意して渡してあげるだけなので、とてもシンプルな実装になります。interruptに特定の条件を渡しておけば、結果がtrueになった場合に差分更新をせずにreloadData()を行うようにできます。クラシルはUI上の1つのセルのサイズが大きいため、例えば、リストの途中の位置などで一定数以上のセルの挿入があったりすると、スクロール位置などが大きくずれてしまったりするため、一定の個数以上の更新が必要な場合などはreloadData()を行うようにしました。data}パフォーマンスに大きく影響を与えている実装箇所の特定ベースのリファクタリングが出来た後にどの実装が問題になっているのかを特定するため、InstrumentsのTimeProfilerを使って重い処理を特定する作業を行いました。使い方はとてもシンプルですが、実際にアプリを動かしながら、カクつくタイミングにメインスレッドで実行している重い処理をひたすら確認していきました。今回のようなMainThreadで実行されている重い処理を見たい場合の確認では以下のような設定を使用していました。IndexPath)のサイズ計算が問題になっているパターンがあると思います。IndexPath)の中で表示するCellのサイズ計算を行っていたため、Instrumentsで見てみるとその中身の関数のときに負荷が跳ね上がっていました。このような、データの内容によってセルのサイズが可変になる場合、データを取得したタイミングで先にサイズ計算を行い、ModelやEntityでサイズを保持するようにしました。することで、Layoutのサイズを返すときには事前に計算しておいたサイズを返すだけになり、メインスレッド上での処理が減るのでパフォーマンスが向上します。Viewの描画に直接関わらない重い処理の修正スクロール途中に急ブレーキがかかるようにスクロールが停止したり、次のページの読み込みが走ったタイミングでカクつきが発生したりしていた原因はViewの描画に直接関係のない重い処理をメインスレッドで行ってしまっていたものでした。この場合も、どの処理がネックになっているかをまずInstrumentsで確認しました。メインスレッドで実行していた重い処理API通信後のJsonのパースログの送信サーバーから取得したデータの変換処理これらの処理をDispatchQueueを使って、適切にバックグラウンドスレッドに引き渡すことによって、読み込み時などにメインスレッドの使用率が急上昇することがなくなり、UIのカクつきが解消されました。まとめUICollectionViewはUITableViewに次いで多くのiOSアプリで使われていると思います。適切に実装していないとパフォーマンスの低下がユーザに見える形で分かりやすく出てしまうため、適度にメンテナンスすることが大事だと思います。delyでは様々なポジションのエンジニアを積極採用中です!ご興味がある方はぜひご連絡ください。11:00iOS版クラシルのフィードを滑らかな動きにするためにやったことTweet関連記事2019-12-16Rails初心者がハマったCapistranoの環境変数こんにちは。delyコマース事業部エンジニアのjohnです。こと...2018-12-12kurashiruが取り組むSEOのおはなしおはようございます。delyでkurashiruのwebグロース全般を担当...2018-12-10AppleのUI実装をさぐるこんにちは。delyデザインエンジニアのJohn(@johnny__kei)です...2018-12-06Androidにいい感じの動きをさせていく話(ViewPagerとその他Viewとの連動)こんにちは。delyでAndroidのエンジニアをしているkenzoです。月別アーカイブはてなブログをはじめよう!delyjpさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。