「家族アルバム みてね」のコア機能であるメディアを一覧できる画面のレイアウトをリファクタリングしました。 iOS14以降で使える UICollectionView の API を活用して、レイアウト崩れに強くシンプルなコードに変更する方法を紹介します。
©MIXIUICollectionViewのAPIを活用してシンプルなコードにリファクタリングするVantageスタジオ みてね事業部プロダクト開発グループ アプリ開発チーム佐藤 光
View Slide
©MIXI自己紹介● 佐藤 光 (@hicka04)● 株式会社MIXI みてね事業部○ 2021/11 中途入社● 二児の父● 「家族アルバム みてね(以下 みてね)」の1ユーザー● 好きなもの○ ラーメン○ ポケモン2
©MIXIアジェンダ● 「家族アルバム みてね」とは● リファクタリング対象と選んだ理由● リファクタリングの進め方● まとめ3
©MIXI「家族アルバム みてね」とは
©MIXI「家族アルバム みてね」とは● 家族で子供の写真を簡単に共有できるサービス● 「世界中の家族の”こころのインフラ”を作る」をミッションに掲げ、現在7言語対応● 利用者数が1,500万人(※)を突破(2023年3月現在)ママやパパの「半数」が使うアプリに5※iOS・Android™ アプリ登録者数、ブラウザ版登録者数の合計
©MIXIリファクタリング対象と選んだ理由
©MIXIリファクタリング対象● アルバムタブ● メディアを一覧できる画面● ページの種類○ 月ごとのメディア○ コレクション (1秒動画などのコンテンツ )● 左右スワイプ or 上部のページタップで遷移7
©MIXIアルバムタブを選んだ理由● 2022/4~ 開発体制をドメインチーム制に変更 (※)○ アルバムの基本機能 (メディアのアップロード/閲覧など)○ メディアを使った商品● アルバムタブに対して手を入れる機会が増えている● 課題: リリース当初から継ぎ足しのコードが多い○ いいものを早く届けるのが困難で、どこかで妥協が発生する■ 修正に多くの工数がかかるのを許容■ 工数がかからないように仕様を調整○ 画面崩れが発生しやすくなっている8※2022年版 みてねを支えるプロダクト開発体制
©MIXIリファクタリングの進め方
©MIXI現状の課題を洗い出し● StoryboardでUI作成○ コードレビューがしやすいコードでのレイアウトを推奨● ViewControllerにロジックが存在○ テストコードが書きづらい● CollectionView○ CollectionViewLayoutを使った地道なFrame計算○ reloadData で全件更新○ セル利用時に非型安全● など…10
©MIXICollectionView周りの解決方法の検討● CollectionViewLayoutを使った地道なFrame計算○ → Compositional Layouts (iOS 13 +)● reloadData で全件更新○ → Diffable Data Source (iOS 13 +)● セル利用時に非型安全○ → Cell Registration (iOS 14 +)11※2023年3月時点でiOS14以上をサポートしているため、現状使える API限定
©MIXIなぜSwiftUIではないのか● みてねでSwiftUIは未導入○ 導入検討中なので、今後活用していきたい● iOS14以上で使えるSwiftUIのAPIでは若干機能不足○ UIだけであればほとんど実装可能そう○ ScrollViewのoffsetを細かく調整する等は難しそうで今回のユースケースに合わない12
©MIXICompositional Layouts (iOS 13 +)● Section / Group / Item を組み合わせて構造化○ 宣言的にレイアウトを作ることができる● サイズ指定○ 親のN%のサイズ■ fractionalWidth / fractionalHeight○ Self Sizing■ estimated● 位置指定○ Groupの並び horizontal / vertical / custom13
©MIXIDiffable Data Source (iOS 13 +)● 更新前後のデータ(snapshot)を比較して差分を取り必要なセルのみリロードできる● 1つのメソッドでデータ更新完結○ reloadItems / reloadSections / reloadData→ dataSource.apply(snapshot)● 差分に応じて自動でいい感じのアニメーション14
©MIXICell Registration (iOS 14 +)● セルの利用が型安全● 従来の方法のデメリット: 実行時クラッシュが発生しやすい○ 事前にセルを登録していないと…○ 表示するセルの取得時にtypoすると…● 各々のプロジェクトでextension実装して解決していた○ collectionView.register(CustomCell.self)○ collectionView.dequeue(CustomCell.self, at: indexPath)● 公式のAPIで実現できるようになった15
©MIXI解決していく● すでにリリース済みのカレンダーUIを例に● 仕様○ 現在のページがわかる○ 表示したい月が増減したときにリロード○ 現在のページが変化したときにリロード○ 1画面内に6セル分入る16
©MIXIUI & Cell作成17● Storyboardで作成されていたため新規で作り直し
©MIXICompositional Layoutsでレイアウト作成18
©MIXIDataSource作成19● セルに対応した型を用意 (Hashable必須)○ ページの種類○ 現在のページかどうか
©MIXIDataSource作成20
©MIXICollectionViewの初期化21● Layout 型と DataSource 型を個別に作っているため初期化部分のコードがスッキリ
©MIXIUIの更新22
©MIXIUIの更新23● 現在のページを変更するときも reload メソッドを使う
©MIXI成果● ほぼ同じ仕様のまま約45%までコードを削減○ +507 -1147● コードの見通しが良くなった○ コード量が少なくなった○ レイアウトの記述が宣言的○ データを更新するメソッドが1つに集約された● 特定の条件下で発生していた画面崩れ解消24
©MIXIリファクタリングに伴う仕様変更● スワイプでのページ遷移中のアニメーションを変更○ Diffable Data Sourceを使う場合UIの状態とデータを一致させた方が実装しやすい○ ピンクのバーではなくOffsetを動かす● チームメンバーやPOに触っていただいた上で許容と判断○ リリース後、この変化をつぶやいている人は見つけられなかった25Before After
©MIXIまとめ
©MIXIまとめ● iOS14以上で使えるUICollectionViewのAPIを活用してシンプルなコードにリファクタリングする方法を紹介○ コード量削減 & レイアウト崩れに強い○ 現状の仕様を全て満たすのが難しい場合も存在する● メディアを表示するCollectionViewも改善中 →○ 今回紹介できなかったAPIも利用予定NSDiffableDataSourceSectionSnapshot● 今後も利用箇所を増やしていってシンプル & 画面崩れに強いUIを作っていきたい27