見出し画像

SwiftUIを使った新設計

こんにちは、Globeeの鈴木 俊裕です。
EMとiOSエンジニアを兼務させてもらっています。

さて、今回は以下の記事の続編になります。

あれから会社が上場したり、映画・ドラマ機能をリリースしたりと色々ありました。全然記事を書く機会を見つけられずご無沙汰してしまいました。
そんな中で最近iOSチームに新しいメンバーがJoinし新しい風が吹き込まれましたので、進捗を共有しようと筆を取りました。お付き合いいただけますと幸いです。


既存設計の課題

既存設計の課題を振り返ってみます。

コード量
ひとつ目は単純にコード量の観点です。Clean Architecture + Fluxのコードはわかりやすく堅牢な反面、冗長ではありました。冗長さに関してはUIKitにも言えることですね。自分はXcodeのCode Snippet機能を多用して頑張っていたのですが、あまりスケールする方法ではないということは感じていました。
現在abceedのiOSアプリはコード行数が20万行以上になっており、これをiOSチーム2人以下で開発しています。私がJoinした2020年と単純に比較するとコード行数は倍になっており、きっとアプリの生み出す価値も倍になっていることでしょう。
とは言ってもやはり、短いコードで済むならその方が良さそうです。

UIKitの開発コスト
UIKitもこれまで長い付き合いで慣れ親しんでいますので、チームとして一定以上の開発速度は出せる自負があります。ただ改めてSwiftUIの簡潔さや柔軟さを知ってしまうと、これは完全にゲームチェンジャーだという認識にいたりました。
やはりPreviewを使った開発はかなり高速で、目で見て細かい体験を調整するという作業がやりやすく感じます。

SwiftUIとの相性
SwiftUIをメインにしていきたいとなった時に、既存の設計との相性が問題になりそうでした。
基本的にSwiftUI .ViewはObservableObjectを求めていますので、RxSwiftで繋がったFluxコンポーネント群はそのままでは役に立ちません。StoreはObservableObjectとして使えそうですが、Store -> Presenter -> ViewControllerと繋がっていますので、Presenterが持ってるロジックをどこかに移動するなど検討が必要そうでした。
というわけで既存の資産を活かしつつ、新しく作る画面に関してはSwift ConcurrencyベースのViewModelで統一することになりました。abceedは新規開発が多いので、こういった新しい技術導入のペースはかなり速くしていけそうです。

チャレンジ精神
あとは会社としても新しいことへのチャレンジは賞賛する文化がありますので、単純にやってみようという気持ちも強かったです。メンバー的にも「大変そう」というよりは「まあなんとかなるだろう」という感覚もありましたので、特に導入に対して難しい議論はありませんでした。
「今後はSwiftUIをメインにしてこういう設計でやりたいと思っているんだけど」
「いいじゃんやろうよ」
このような大胆な意思決定のスピード感はやはり大事にしたいですね。

新設計

主なコンポーネント

  • ViewModel

  • Service

  • Repository

今のところそこまで厳しいレイヤー分けルールは敷いていないですが、ざっくりViewModelから使う共通ロジックをServiceにまとめ、データの永続化やAPI呼び出しをRepositoryにまとめているという構成です。

実装のポイント

チームとしてはある意味「1周回ってViewModelに戻ってきた」感覚があります。経験上ViewModelはうまく使わないと大変なことになりますので、以下の点はコーディングルールということにして気をつけています。

  • FluxでいうActionとなるインターフェース関数は戻り値を持たず、必ず呼び捨てにする。

こうすることで処理が単方向的にまとまるので、複雑さを下げることができます。

Button {
    viewModel.onTap(at: n)
} label: {
    Row(number: n)
}

また画面遷移はUIKitで行っていますので、以下のような形でViewModelから画面遷移のイベントを発行し、ViewControllerがそれを購読することで画面遷移を行うようにしました。

    viewModel.transition.receive(on: DispatchQueue.main).sink(receiveValue: { [weak self] transition in
        let vc = ViewController()
        self?.navigationController?.pushViewController(vc, animated: true)
    })
    .store(in: &cancellables)

上記のコードは以下にもアップしてあります。
https://github.com/globeejp/abceed-UIComponents/tree/main/SampleApp/ListView

コーディング試験

上記まとめ始めた段階で気づいたのですが、少しコーディング課題をアップデートする必要がありそうでしたので、一部SwiftUIの指定を追加しました。まだまだUIKitも現役ですので、会社としては両方できる人を募集しています。腕に覚えのある方、ぜひぜひご応募ください。
コーディング試験については別途機会があれば記事にしようかと思います。

まとめ: 今後の展望

いかがだったでしょうか。何かの参考になれば幸いです。
ちょうど週末はiOSDCがありましたが、個人的に忙しくてリアルタイムで見れなかったので、タイムシフトを楽しみにしようと思います。
それではみなさまごきげんよう。


♦︎ 採用情報

現在株式会社Globeeでは「エンジニア職種」を中心に採用活動を積極的に行なっております。
弊社が「教育」にかける思いや「ものづくり」の考え方について、
お話が出来ればと思いますので、皆様お気軽にご応募下さい。



いいなと思ったら応援しよう!