この記事は NewsPicks Advent Calendar 2024 の11日目の記事です。
ソーシャル経済メディア「NewsPicks」でiOSエンジニアをしている金子です。
今年はじめにTCA勉強会を主催し、多くの方々にご参加をいただくことができました。
この勉強会をきっかけに、NewsPicksといえばTCAという認知を多少なりとも広げることができたのではないかと思っています。
そのすぐ後、Findyさん主催のイベントや、Software Designの記事にて、TCAへのリアーキテクチャについて発表を行いました。
https://findy.connpass.com/event/315494/findy.connpass.com
しかし、その後ほとんど情報発信することができておらず、あれからどうなってんの?と思われているかもしれません。
もちろんサボっていたわけではありませんよ。
そこで今回は、NewsPicks iOSアプリのリアーキテクチャの現在地についてご紹介できればと思います。
TCAの最新バージョンに対応
TCAにアップデートがあれば、すぐに追従するようにしています。現在は本稿執筆時点の最新バージョンである1.17.0
に対応しています。
ただし、TCAは後方互換性を大事にしてくれるとても親切なプロジェクトなので、最新バージョンを使っていること自体に大した意味はありません。
僕らはTCAをかなり初期から導入しているため、画面によって使用しているAPIが異なるという状況がしばらく続いていました。
コツコツとマイグレーションを進め、先月ようやくTCAで実装されたすべての画面のマイグレーションが完了しました。
今後僕らのチームにジョインする方が新しくViewStore
について学ぶ必要はありません。
Swift Package中心のプロジェクト構成へ
Swift Package中心のプロジェクト構成は、iOSのプロジェクトではもはや定番といっていいのではないでしょうか。
NewsPicksでは元々XcodeGenでプロジェクトを構成し、Embedded Frameworkでモジュールをいくつかに分けていました。モジュールはDomain層、Infrastructure層、Presentation層といった分け方をしていて、TCAで新しく実装した機能はPresentation層あるいはメインターゲットにまとめて入っているような構成でした。
しかし、TCAでSwiftマクロが導入されたあたりからビルド時間が著しく増加してしまい、開発にかなりの支障をきたすようになってしまいました。
Reducerの実装ではマクロを多用しますが、Reducerの数が多くなるとビルド時間がどんどん長くなっていってしまうのです。
機能単位でモジュールを細かく分けることで、Reducerが複数のモジュールに分散し、結果としてビルド時間を速くすることができます(差分ビルドはもちろん速くなるし、トータルのビルド時間も速くなります)。
ただ、Embedded Frameworkの形でモジュールを増やしていくとアプリの起動時間が遅くなるという懸念があります。
そこで、Swift Package中心のプロジェクト構成にチャレンジしようと思い立ちました。
XcodeGenとSwift Packageでプロジェクトを構成する
Swift Package中心のプロジェクト構成の作り方は、すでに多くの方がブログで公開してくれていますので、それらを参考にすればすぐにできます。
ただ、NewsPicks iOSアプリはすでにリリースから10年以上が経過しており、リアーキテクチャが進んでいるとはいってもまだまだ古いアーキテクチャのコードがたくさんあり、それらをメインターゲットから剥がす作業はなかなかコストがかかります。
メインターゲットでの作業ではまだまだXcodeGenの恩恵(コンフリクト回避)を受け続けたい、でも新しいコードはSwift Packageで管理したい。
検証した結果、この2つを両立するやり方がみつかりました。
詳しくはQiitaに記事を載せていますので、気になる方は参考にしてみてください。複数のやり方を載せていますが、NewsPicks iOSアプリでは最後にある2024/04/03追記 単一のパッケージで同じことを実現するのやり方を採用しています。
Xcode Previewsとミニアプリで爆速開発できるようになった
Swift Package中心のプロジェクト構成になったことで、開発スタイルが大きく変わりました。
Xcode Previewsの活用と、Featureごとに用意するミニアプリでの開発です。
前述したようにNewsPicks iOSアプリは10年もののアプリであり、かなり規模の大きなアプリになっています。
以前もEmbedded Frameworkでモジュールを分けてたとはいえ、Xcode Previewsを使おうにもビルド時間がかかりすぎてしまい、まともに使える状態ではありませんでした。
しかし、Feature単位でモジュールが細かく分かれたことでプレビュー時のビルド対象のファイルが少なくなり、快適にプレビューできるようになりました。
下図はXcodeのプロジェクトファイルで定義しているターゲットの一部ですが、例えばFeaturedFeaturePreview
はFeaturedFeature
というFeatureのみに依存したミニアプリのターゲットです。
FeaturedFeature
の画面をプレビューするときはSchemeをFeaturedFeaturePreview
に指定します。こうすることで、FeaturedFeature
だけがビルド対象となるため、ビルド時間が短くなります。
ちなみに、プレビューのコードはFeaturedFeature
の中ではなく、FeaturedFeaturePreview
ターゲット側に置いています。Featureのコードとプレビューのコードが別々のファイルに実装されているということです。
もちろんFeatureのコードとプレビューのコードが同じファイルにあるのが理想なんですが、FeaturedFeature
から別のモジュールの画像アセットを参照している関係で、プレビューがクラッシュしてしまうのです。以下の記事で詳しく書かれていますが、これの解決策がXcodeプロジェクト側のアプリターゲットにプレビューのコードを置くことです。
多少不便ではありますが、プレビューを使って開発できるというのは僕らにとっては革命的な変化です。
また、FeaturedFeautre
だけに依存したミニアプリをビルドしてシミュレーターや実機にインストールすることで、単一機能だけを持ったアプリを動作させることができます。
アプリ全体をビルドしなくて良いのでビルド時間は短くなり、また目的の画面に移動する手間も省ける(起動したらすぐその画面になる)ので、開発サイクルが圧倒的に速くなります。
ついにCocoaPodsを消すことができた
長年お世話になってきたCocoaPodsですが、先日ついにサヨナラしました...!
SPM対応しておらずメンテが止まっているライブラリや、負債が多くなかなか手がつけづらい場所で使われているライブラリなどがしぶとく残っていたのですが、前者についてはフォークしてSPM対応させ、後者については地道にリファクタを行い、不要なライブラリを消していきました。
目指すべくはApple純正ツールのみで構成された開発環境。XcodeGenもいずれはサヨナラし、Xcode CloudでCI/CDする世界を目指していきたい。
残っている課題
Stateドリブンなナビゲーション
NewsPicks iOSアプリは元々UIKitベースで作られているため、画面のSwiftUI化は進んでもナビゲーションはUIKit(UINavigationController)で行っています。
TCAにナビゲーションコンポーネントが追加されてからは、一部ではStateドリブンなナビゲーションを採り入れるようになりましたが、UIKitではStack-basedナビゲーションがずっとできない状態だったので、若干無理矢理感のあるナビゲーション実装になってしまっているところがあります。
しかし、TCAのバージョン1.13.0でついにUIKitでStack-basedナビゲーションをするためのコンポーネントが追加されました。
このコンポーネントの追加により、UIKitベースアプリとSwiftUIベースアプリでできることの差がほぼほぼ無くなったと言えます。
すべてのナビゲーションをStateドリブンにすべく、近々本格的に対応を進めていく予定です。
アプリライフサイクルのSwiftUI化
アプリのエントリポイント周辺のコードがかなり秘伝のタレ化してしまっており、現在このあたりを地道にリファクタしているところです。
リファクタすることによって起動速度の改善、コードの可読性向上を狙っており、ゆくゆくはアプリライフサイクルをSwiftUI化していこうとしています。
おわりに
気づけばNewsPicksに入社して5年以上が経過しました。
入社当時からiOSアプリの開発に関わらせてもらっていますが、当時と比較すると今の開発環境の快適さは桁違いです。
NewsPicksというサービスは開発が非常に活発で、アプリへの改善要望が社内からひっきりなしにやってきます。
そうした要望に応えながら限られたiOSエンジニアリソースでリアーキテクチャをしていくのはなかなか大変ですが、チームで協力してここまで進んでくることができました。
まだ大きな課題は残っているので、引き続き改善を進め、TCA製アプリの代表の一つとして胸を張れるようなアプリにしていきます。