iOSDC2023に参加しました
開発本部エンジニアリング部の小笠原です。普段はTeachme Biz iOSアプリの開発に携わっています。
今年(2023年)もiOSDCに参加してきました。
今回は複数のチームメンバーでの参加で、食事の時間にも同僚とトークの感想戦ができたのが大きかったです。今年導入された展示ルームの話題から派生して、弊社で開発しているTeachme Bizアプリでも何かできないかといったことも話していました。
ちょっと技術的な話題も
セッションについて、1つだけここで触れておきたいと思います。
「VIPERアプリにSwiftUIを導入したら、View層の責務がより分離できた話」についてです。
VIPERアプリにSwiftUIを導入していった経緯が、弊社のiOSアプリ内でここ1年取り組んでいたことに重なり、とても共感を持って聴くことができました。
実装もほぼ同じ手法だった(のである種の安心感を得た)のですが、一点相違を感じた箇所があります。
それは、「SwiftUIのViewからviewModelの値を更新しない」というルールを設けた、という点でした。
弊社のTeachme Bizアプリの場合、下記のサンプルコードのように、ViewからPresenterを参照させており、ViewModelは使用していません。
struct FooRootView<Presenter: FooPresentation>: View {
@ObservedObject var presenter: Presenter
var body: some View {
VStack {
Text(presenter.viewData.title)
...
}.onTapGesture {
/* viewからの更新・イベント通知は、以下の手段を用いる:
- presenterのメソッドを呼び出す
- デリゲート先を設定して、デリゲートメソッドを呼び出す
- Viewのプロパティとして設定したクロージャを呼び出す
*/
presenter.didBarTap()
}
}
}
@MainActor protocol FooPresentation: ObservableObject {
var viewData: FooRootViewData { get }// get onlyに制限することで、viewからの変更を防ぐ
func didBarTap()
}
final class FooPresenter: FooPresentation {
// viewDidLoadなどで通信を行い、viewDataを更新する
// private(set)にしなくてもViewからの更新は防げているのですが、private(set)にしていることが多いです
@Published private(set) var viewData = FooRootViewData()
func didBarTap() {
// viewから呼び出されるメソッドは、wireframeの画面遷移メソッドを呼び出すことも多い
wireframe.showBaz()
}
}
Presentationプロトコルを介してViewとPresenterを繋ぎ、ViewからPresenterの状態を変更するコードはコンパイルできないようにしています。
ViewDataはイミュータブルなプロパティのみを持つことが多く、その更新はPresenterのみが行います。
ViewModelの持つ責務のうち、静的な画面表示用の情報はViewDataが、それ以外の動的な責務をPresenterが持つイメージでしょうか。
この設計によって、Single source of truthが実現でき、実装時の考慮が削減できていることが今回再確認できました。
この方針は、SwiftUI導入以前からあったものを踏襲しているのですが、SwiftUIが導入されてからも一定機能していると感じます。
個人的には、VIPERアーキテクチャは画面を増やすごとに追加されるファイルが嵩むまどろっこしさがあるものの、機能追加などで既存の画面に手を入れる際に迷うことが少なく、実装者ごとの差異も小さくなるため保守性が高いと感じます。
…といったことをAsk the speakerのコーナーで話せたら良かったのですが、うまくスピーカーの方と遭遇できず(おいおい)、ここに記している次第です。
最後に
今年も一年分の気力を充電できました。会場に行けて良かったよ、、、来年もまた会いましょう〜。
スタディストでは “伝えることを、もっと簡単に” していけるメンバーを広く募集しています。
iOSに限らず採用を行なっておりますので、興味を持った方はEntrance Book等みていただけますと幸いです。