9年もののiOSアプリのプロダクト改善を1年ちょいやったのでまとめる

f:id:iseebi:20200220235429j:plain *1

昨年の頭に転職して、ピリカという会社にお世話になっている*2。科学技術の力で環境問題を解決するという目標を元に、ごみ拾いSNS ピリカや、ITに限らず様々な技術を使った調査プロジェクトなどのプロダクト/プロジェクトを推進している会社だ。

ピリカとの出会いは、2011年5月のスマートフォン勉強会@関西#15。まだ立ち上がった当初に社長の小嶌さんに登壇してもらって、それ以後もFacebookで繋がっていて、途中クラウドファンディングの時に応援したり、ゆるくいいねを送りあう関係だった。前の会社で東京転勤した頃に一度お声かけしてもらって、1年後くらいに転職の機会が巡ってきて相談したところ、条件がうまくマッチしてお世話になることになった。

メインのお仕事は、ピリカSNS*3iOSアプリ開発。正式入社からしばらくして、iOS版の開発をひとりで引き継ぐこととなった。

2011年5月にあったすまべんの頃はまだTitaniumで作られていたアプリは、gitのコミットログによると同年9月にネイティブ版の開発が開始されたらしい。ログの中にはTwitterのタイムラインで見たことがある方もいらっしゃったり、歴史の深さを感じる。

もちろんその時その時でのベスト選択をされてきたのだろうけど、時代も人も変わりながら開発が進んできていて、古いライブラリへの強い依存、新しい方式への移行が中途半端で同じ機能が複数実装されているといった状態だった。だからといって作り直しするというのは幾度となく大改築で苦労した身としては回避したかった。

幸いなことに、受託中心だったこれまでとは違い、長く継続的にアプリのプロダクトと向き合うことができるので、長期戦を見越して少しずつ改築していくことにした。

今回は、この1年でやってきた改善を紹介してみようと思う。

アップデートの方針

一気に書き直すということは避けることとしたが、デザイン的にも、機能ごとに画面のデザインやフローを見直していきたいという流れになった。 そこで、画面のデザインを置き換えることになった機能ごとに、新しい方針に従ったSwiftコードに置き換えていく形をとった。

他にもあるが、具体的には以下のようなことをした。

まずはCIを整備

僕がチームに入ったタイミングは、GitHubでソースは管理されているものの、ビルドは手動だった。手動ビルドではリリースビルドを作る際にミスを起こしてしまう可能性が高まってしまうので、何かしらの自動化は必要であると考えた。

CIサービスをいくつか検討して、最終的にBitriseとApp Centerの一騎打ちだったのだが、当時iOSのExtensionを使った機能が検討されていたもののApp Centerではビルドできなかったため、Bitriseを採用することとなった。

Pull RequestでDangerやLintチェックがかかり、developマージで検証環境Adhocの作成、masterにマージされると本番系のビルド、Tagを打つとApp Store Connectへアップロードされるといった、標準的なセットアップとなっている。

警告の除去

チームに入った当初は、ビルドすると100個以上の警告が出るような状況だった。 警告を大量に残していると、本当に注意しないといけない警告が出ているときに気づくことができないという問題がある。

直接参照されているライブラリが古くてDeprecatedの警告を出していたり、古いObjective-Cの記述が残っていたりなどといった原因があったが、警告の内容を読みながら順次つぶしていった。ライブラリ由来のものは無視できるようにプロジェクトの設定を加えていき、一旦通常のビルドでは警告0件になるようにした。

これのおかげで、廃止したいメソッドにDeprecatedのフラグをつけて警告を出し、順次使用箇所を修正していくといった作業も可能となった。

static メソッドを固めた Util クラスからextensionへ

処理の共通化は static メソッドを集めた Util クラスを中心に行われていたが、操作対象がオブジェクトなのであればextensionのメソッドに書き換えた方が読みやすいと思う。

新しいコードではなるべくUtilクラスを使わない形に変え、共通化が必要なものはextensionにしていった。まだすべてはなくせていないが、書き直しするたびに定義ジャンプで存在を確認し、参照がなくなったものから順次消している。

特に日付関連については、日付がAPIから戻される文字列のままViewレイヤーまで取り回され、必要になるたびにDateFormatterを生成していた。文字列のまま管理されていると変換などに苦労するし、DateFormatterの生成コストも大きい。そしてそのコンテナはDictionaryで型がない。

新しく書くコードではモデルクラスを作ってDateに変換して取り回すようにし、DateFormatterを使ったフォーマットはDateに対するextensionで定義するようにして、フォーマットの定義を一箇所に集約することができた。

内部処理のレイヤー分け

ViewControllerに直接通信処理が書かれているところが多数だった。ViewControllerに通信が書かれていると、表示と処理が一体化してその後のメンテナンスがやりづらくなる。

新しく書くコードではViewControllerの下に、機能ごとにManagerと呼ぶオブジェクトを配することにした。 いきなりClean Architectureまで取りかかるのはコードが整理されてなくて難しいと判断して、一旦は1層だけにした。

Manager内のAPI呼び出しやデータ管理についてはextensionに切り出して、次のステップとして別クラスに分割することもすぐにできるように準備している。

また、DIを意識して、Managerはクラスの決まった箇所にletで定義、コンストラクタでのみスーパーオブジェクトから取得して初期化して良いというルールにしている。

通信機能

着手した当初は2種類の通信APIが共存していた。

  • 主にObjective-Cから使っている、NetworkUtilという名前のASIHTTPRequestのResponseを直接ブロックで返すAPI
  • Swiftから使っているMoya

書かれた時期によってはSwiftからもNetworkUtilをガンガン使っていたが、新規で書くSwiftコードからはMoyaで統一するようにして、順次NetworkUtilへの参照を外していった。

また、MGTwitterEngineという古いTwitter OAuthを通すライブラリにも強く依存しており、これが大量に警告を生み出していた。 こちらについてはTwitterKitを入れようとしたが廃止になっていたので、代用となるライブラリを自分で起こすことにした。 業務の成果物をオープンソースで出すことはずっとやりたかったことの一つで、社会人になって13年、ここでようやく果たすことができた。

github.com

BaseViewControllerの廃絶

いわゆる、BaseViewControllerに多くの処理があり、BaseViewControllerの実装を回避するためのオーバーライドが存在するような形になっていた。BaseViewControllerの問題についてはiOSアプリの設計でBaseViewControllerのようなのは作りたくない - Qiitaの通り。UITableViewControllerなどのUIViewController派生からのViewControllerが作れないか、それにあわせて全部用意しないといけないという問題もある。

新しいコードからは、すべてUIKitのViewControllerから直接派生するようにして、共通化したい機能はprotocol extensionを使ったTraitのような形で実装していった。(Swiftのprotocol extensionでmixin的なものを実現する - Qiitaを参考にした)

例えば、以下のようなTraitを作った。機能ごとの共通処理なども同じようにTrait化したりした。

  • シェアダイアログ表示
  • メーラー表示
  • Pull to Refresh

結果

そうしてやってきた結果が結実したのが、先日リリースされたメジャーバージョンアップ。

blog.pirika.org

僕が入った当初はコードに対するSwiftの割合が35%程度だったものが、92%にまで置き換わった。起動速度も実行速度も改善したし、クラッシュも大幅に減ったが、まだまだ改善は道半ば。表示と処理の癒着が分離されて整理されてきただけである。やってきたのは普通と言われる内容だとは思うけど、劇的に作り替えなくても少しずつ改善を積み重ねればよくできるという実感を得ることができた*4。今後も地道に改善を続けていきたい。

これ以外にもピリカSNSの開発ではサーバーサイドの開発もやっている。ピリカSNSのバックエンドはGAE/Pythonとなっていて、転職にあたってPythonを覚える必要があった。以前やったNissanConnectにさわやかの待ち時間をしゃべらせるものは、実はそのための習作だった。そのおかげで1時間ほどレクチャーしてもらった後はスムーズに開発に入れたと思う。

ちなみに、ピリカには週4でお世話になっており、残りの日は別の会社の取締役としての活動をしている。 「週4勤務で働かせてください、あとは別の仕事をします」という条件だと東京でも職探しはかなり大変だったのだけど、ピリカという会社は勤務時間もフレックス*5で調整が効きやすいだけでなく、メンバーにおける副業の人の割合が高く、このような働き方を受け入れてくれる土壌があったのもよかった*6。 環境問題は分野が広く、まだ自分たちも手を付けれずにいることもあり、それに伴って必要なシステムや機材もまだまだ際限なくあり、ものを作りたい欲を満たすにはとても十分な環境だと思う*7。実際プログラミングだけではなく、調査機材などのハードウェア開発もやるようになった。

なお、もうひとつの会社ではTypeScriptとAWSでフルサーバーレスのWebホスティングシステムを開発しているが、こちらについてはまた別の機会に…。

*1:夏合宿会場から撮った景色。合宿と言いつつ泊まりではないのが恒例らしい。

*2:厳密には、2018年の11月くらいから副業的に入っている。

*3:会社と明確に分けるためにこう呼んでいる

*4:1人でやって残業もほぼしなかった

*5:コアタイムなしのフレックスはとても人間に優しいし、会社的も残業代を最適化できる良い仕組みだと思う。

*6:実験的に〜とか特別に〜みたいな形だと軋轢が出るはずだし…。

*7:元々科学館巡りが好きということもあり興味としても近かった