概要
pixiv主催の「iOSアプリ設計ナイト」という勉強会に参加してきたので、雑にメモ。
https://pixiv.connpass.com/event/112561/
セッション
「2つの同期 4つの状態」by ダンボーさん
- 資料
- アーキテクチャにはふたつある
- GUIアーキテクチャ
- MVC、MVP etc...
- システム・アーキテクチャ
- クリーンアーキテクチャ etc...
- GUIアーキテクチャ
- 2つの同期
- オブザーバー同期
- フロー同期
- オブザーバー同期
- 監視によるデータ同期
- データフローが追いづらい
- フロー同期
- 手続きによるデータ同期
- データフローが追いやすい
- 遠い場所にあるコンポネント同士の同期が難しい
- 4つの同期
- Screen State
- Viewの状態
- Presentation State
- Presenter、ViewModelの状態
- 表示に使うための中間データ
- 例:Twitterのいいねボタン
- Session State
- Modelの状態
- オンメモリで管理するRecord Stateのコピーのデータ
- Record State
- DataStoreの状態
- 永続化状態
- Realmなど
- Screen State
- どのデータをどのデータと同期するか
- クリーンアーキテクチャの役割で対応するStateを見る
- イメージしやすくなるため登場したが、本来はシステム・アーキテクチャ
- クリーンアーキテクチャの役割で対応するStateを見る
- PTSをどう実現するか
- ScreenとRecordをどう同期するか
- State同士の距離と同期コスト(=エンジニアがコードを追うコスト、書くコスト)
- 中間層を抜くとコストは低くなるが、Fatになる
- State同士をつなぐ様々なパターン
- Screen Stateと他のどのStateを同期するか
- 要件、コストなどを考慮して、君だけのオリジナルのアーキテクチャを手に入れる
- パターンから見る
- APIから取得したデータを画面に表示するだけ
- ScreenとSessionを直接同期させればOK
- APIから取得したデータが必ず同期するわけではない
- 同期するかしないかを管理するため、中間データで管理する必要がある
- Presenterに一度保持して処理をする
- MVP
- 同期のパターンが2つあって管理するとややこしい
- MVP(Passive View)
- フローで同期するので追いやすい
- 間にPresenterを置く必要があるので、ちょっと手間
- APIから取得したデータを画面に表示するだけ
- GUIアーキテクチャを理解する
- 距離の調節によって責務分割の仕方が変わる
- 距離は同期方法と中間処理によって異なる
- まとめ
- 各アーキテクチャパターンで状態の持ち方と同期方法に違いがある
- 必要な状態が増えてきた歴史があって、それぞれの同期方法の違いで各種アーキテクチャパターンが生まれているといってもいい
「橋の下で安心して寝るための『iOSアプリ設計パターン入門』」 by kameikeさん
- 橋の下で寝ている自分がたどり着いた橋の下で寝ているということは自信をもっていい
- ローマ時代は橋を作った後に橋の下で寝なければいけなかったらしい
- 「反脆弱性[上]――不確実な世界を生き延びる唯一の考え方 」から引用
- Palcyの実務の話
- マンガアプリ
- 数年間保守をしていくことになる
- MVVMでもよりより形を求めて変化していった
- テスタビリティにするなど
- 漸進的成長のTips
- プロジェクトを縦に割る
- 縦割りのフォルダ校正
- 画面ごとに必要なものをまとめた
- 横割りだとどのファイルが最新で、更新対象かわかりづらい
- ChangeLogと対応が取りやすい
- 各画面ごとにReadme.mdが置けるので、現状の背景を他の人に伝えやすい
- 縦割りのフォルダ校正
- 腕試しのBLoCパターン
- 小さく導入して効果を実感できるオーバーヘッドが少ないのでオススメ
- 影響範囲が限定的なので試しやすい
- BLoCを導入したことで発展する先の道筋が見えやすくなった
- プロジェクトを縦に割る
「iOSアプリ設計パターン選定」 by d_dateさん
- 同設計すべきか
- 機能要件はなに
- 非機能要件はなに
- チームのスキルやリソースは
- リードできるエンジニアはいるか
- そのアーキテクチャを好きになれるか
- 非機能要件を想像する
- 品質レベル
- どのぐらいの開発期間
- 通信失敗時のハンドリング
- エラーパターンはどのぐらいあるか
- 設計パターンに正解はない!
- マッチすると思ったものを使う
- ドメイン駆動設計(DDD)
- Repository
- 詳細はtakasekさんのQiitaの記事を見て
- 永続化の手段
- Repositoryの中で通信したものとCacheとを判別する処理を書く
- Cacheはファイルでもいいし、DBでもいいし、UserDefaultsでもいい
- Repositoryの中のコードは公開されないので、好きに書いていい
- REST APIとCachaを区別しない=MockでもOK
- テストが書きやすい
- Repository
表示ロジックにおけるテスタビリティの獲得方法 by いしかわさん
- 資料
- テスト書きづらい、メンテがつらい、開発速度が遅い…これらはテスタビリティが低いせい。ベストではないがこれまで自分たちがやってきたことを発表
- 画面内のどういうところがテストできるか
- 画面表示時
- インジケーターを表示
- 読み込みを開始
- 読み込み完了時
- 星のタップ時
- 画面表示時
- 一連のフローを上から下まで一気にテストするのは大変(かなりコストがかさむ)
- UIテストは実装も実行も高コスト
- UIの要素を通じて状態を検証する必要がある
- 待ちが多くなるため反復実行に向かない
- タイムラインの制御が難しい
- 連打などの込み入った状況を再現できない
- テスト対象の状況を用意しづらい
- 外部のシステムの応答を切り替える必要がある
- 環境によって不安定な結果になる
- UIテストは実装も実行も高コスト
- 解決:UIテストは実装も実行も高コスト
- 検証しやすいモデルで表現する
- テスト用のデータをstructで用意する
- 画面の状態を表現するプロパティ
- セルを表現する型
- テスト用のデータをstructで用意する
- 状態を検証しやすくなった(実際の表示は検証していない)
- UIテストでなければ検証できないものもある
- 状況に応じて使い分けるとよい
- 検証しやすいモデルで表現する
- 解決:タイムラインの制御が難しい
- 仮想時間上でイベントを扱えばよいのでは
- 仮想時間は単なる数値なので自由に制御できる
- 仮想なので100秒も一瞬
- 結果も仮想時間で検証できる
- 解決:テスト対象の状況を用意しづらい
- 依存をプロトコルにしてスタブ化する
- APIClientをProtocolにする
- テストのときにスタブのAPIClientに切り替えることができる
- スタブだけなら実装は簡単
- テストケースごとに任意のレスポンスを返せる
- テストしやすい土俵で戦おう
- サンプルコード
「iOSアプリ設計の何がつらいのか」 by takasekさん
- 状態とフローのつらみ
- オブジェクトの生存期間が長いからつらい
- サーバーサイドと違ってアプリがずっと生きてて処理を待ち続けている
- 処理が非同期に行われる
- スレッドをブロックしてはいけない
- 状態がミュータブルだから辛い
- 純粋関数にすればよい?
- どこかしらで副作用を取りまとめる必要がある
- オブザーバーパターンで解決する
- オブジェクトの生存期間が長いからつらい
- 要求変化のつらみ
- つらいところ
- 要求は変化する
- どこが壊れるかわからない
- リリースしたら直せない
- 壊れたバージョンをリリースするといつまでも使われる可能性がある(意訳)
- テストで解消しよう
- コストが上がる気がするが小さく試せるので結果的にコストが減らえる(意訳)
- リファクタリングで解消しよう
- テストがあることでリファクタリングできる
- 画面とモデルで求めるものが違うのでつらい
- つらいところ
- 画面によって興味が違う
- 入力と出力がn:nになる
- 関心の分離
- コンポーネントの興味を小さくする
- 適切にレイヤー化する
- インターフェイスは小さくする
- つらいところ
- 外部との互換性がつらい
- つらいところ
- マイグレーションがつらい
- スキーマを定める
- レスポンス設計
- バージョニングする
- 新しいキーはoptionalに
- つらいところ
- つらみと戦う
- つらみをたおす
- 武器を知る
- 設計パターン
- 武器の使い方をしる
- プラクティス
- 原則
- 武器を知る
- つらみからにげる
- 分散コンピューティングの原則
- 手段
- 状態を減らしてもいい
- ごちゃごちゃした画面でなくていい
- 永続化しなくていい
- つらみをたおす
感想
石川さんのセッションがとても良かったので、資料を再読しようと思う。