LUUP iOSアプリ開発環境の現状確認 2024年8月
こんにちは、iOSアプリエンジニアの茂呂(@slightair)です。
この記事はLUUP iOSアプリ開発環境の現状確認と題して、我々の開発環境の状況を紹介するものです。1年半くらい前にも自分がiOSアプリ開発環境に関する記事を書きましたが[1]、それからのアップデートになります。
LUUPのサービスを成長させながら、同時に開発環境をどのように前進させているのか紹介させてください。
開発体制について
現在、LUUPのiOSアプリ開発に関わっているiOSアプリエンジニアは、正社員3名と業務委託2名になっています。フルタイムでアプリ開発にコミットしているのは正社員の3名であり、業務委託2名は副業という形で本業の合間に関わっていただいています。
また詳細は省きますが、アプリ開発に関わるエンジニアには、WebAPIの開発を担当するサーバサイドエンジニアなどもいます。前回報告した時期に比べ、サーバサイドにも頼もしいメンバーが増え、開発体制の強化が進んでいます。
サポートするiOSバージョンについて
2024年8月現在、LUUPのiOSアプリはiOS16以降をサポートしています。
LUUPアプリは基本的に直近の2バージョンをサポートするよう心がけており、新しいOSバージョンがリリースされてから半年以内くらいに古いものを切る検討をしています。
理由として、iOS SDKの新しいAPIをバージョン分岐なく利用できるようにしたり、リリース前の検証環境を絞って素早く安定したリリースを届けられるようにする狙いがあります。
サポートOSバージョンを見直す際にはOSバージョンごとのユーザー数の推移などを考慮していますが、なるべくユーザーに不都合がないようにすることと安定したリリースを続けられるようにすることのバランスを取って判断しています。
アーキテクチャやモジュール構成について
アーキテクチャやモジュール構成は前回の記事からあまり大きく変化はありません[2]。
モジュール分割するリファクタリングは落ち着き、新規機能は粒度を見て新しいUIモジュールを作り開発を進めるのが基本の実装方法になりました。
XcodePreviewsやスナップショットテストを書きやすくするために、ビジネスロジックを実装するServiceクラスを都合良く加工して注入する仕組みを整え、大部分の画面実装で利用できるようにしています。
現在は以下のようなモジュール構成になっています。
- アプリ、Extensionモジュール
- App - アプリ本体
- NotificationServiceExtension
- LUUPLiveActivity
- LUUPWidget
- コアモジュール - アプリ全体で広く利用されるような基礎となるモジュール群
- Common - アプリ全体にまたがる定数などをまとめる
- DesignSystems - UIコンポーネントや色を定義する
- Entities - ドメインモデル、データモデルを定義する
- Libraries - Extension、ユーティリティメソッドなどを定義する
- Resources - 文字列や画像リソースを定義する、SwiftGenを利用してコード生成
- Services - Firebaseなどへの通信やUserDefaultsの読み書き、ビジネスロジックを実装する
- Store - 複数の画面をまたいで状態を保持するものを定義する
- UseCase - 複数のServiceを束ねた大きなビジネスロジックを実装する
- ViewControllerFactories - UIモジュールをまたいだ画面呼び出しを実装する
- UIモジュール - 機能単位でまとまった少数の画面を実装するモジュール群
- お気に入りポート
- プロフィール
- ライド履歴
- など...
- テストターゲットモジュール
- Tests - ユニットテスト、スナップショットテスト、E2Eテスト
画面実装について
前回からの大きな変更点として、ついにLUUPアプリではInterfaceBuilderの利用がほぼ0になりました。
LUUPプロジェクトでは画面実装にInterfaceBuilderを利用しない意思決定をしており、書き換えを進めていたものが完了しました。
LaunchScreen.storyboardだけが唯一残っていますが、これもいずれ消せそうです。
モジュール分割とあわせてリファクタリングの進行状況をメトリクスとして記録していましたが、StoryboardやXibファイルがなくなったときは0が並び、とてもすっきりしました。
現在のLUUPアプリのモジュール一覧
新規画面ではSwiftUIの利用が増えています。
テキスト入力が伴う画面やマップ上に表示するような画面要素は引き続きUIKitで実装していますが、基本的にはSwiftUIで実装できないか試すようにしています。
iOS16以降をサポートするようになったので、UIHostingConfigurationを使ってCollectionViewを用いている部分の書き換えも進めています。
余談ですがXcode15でPreviewマクロを使ってPreviewsを実装できるのがとても便利ですね。
UIKitで実装された画面であってもUIViewControllerRepresentable/UIViewRepresentableを書かなくてもよくなったのがうれしいです。
LUUPプロジェクトではXcodePreviewsを積極的に使って画面実装を進めています。
Swift Concurrencyの利用について
Swift Concurrencyの導入が進み、非同期処理・並行処理の記述のほとんどで利用するようになっています。
前回の記事を書いたときにはPromiseKitを利用している箇所が多く残っていましたが、これの置き換えが完了しました。Combineの利用箇所も消えています。
言語の進化に会わせて記述方法を変えたり、サードパーティーのライブラリーに頼らないようリファクタリングしていくのは、持続的なアプリ開発を進める上で重要だと考えています。
PromiseKitに関しては、2024年5月のPrivacyManifestの対応方針を探っているときに利用ライブラリーの棚卸しする、依存を極力減らすという意味でも対応が進みました。
スナップショットテストの拡充
スナップショットテストの導入による、画面表示崩れなどのリグレッション検出にも力を入れてきました。
テスト実装にはswift-snapshot-testingを利用しています。
特にこの半年はスナップショットテストの対応画面を増やすことを目標にしており、7割程度の画面をカバーするようにできました。
Webコンテンツを表示している画面やマップ画面など、どうしてもスナップショットテストを記述するのが難しい部分もあるため、すべての画面で対応することは目指していません。
それでも、リリースのたびに意図しない変更が起きていないか、ある程度機械的に検出できるようになりました。
リファレンス画像の管理方法や、テスト結果の表示方法などに課題があり、改善できるところはまだありそうなので今後もスナップショットテストを有効活用できるように手を入れていくつもりです。
スナップショットテストの結果表示の例(アプリ全般でContinuous Corner Curveを導入した時)
ダークモードやアクセシビリティについて
最近LUUPアプリもダークモードに対応できました。
LUUPアプリは基本的には屋外で利用されることを想定しており、時間帯によって適切な表示モードが異なると私たちは考えています。例えば夜間にマップを見る場合は、ダークモードのほうが見やすいでしょう。
そのため、夜にまぶしい画面を見ないで済み、目に負担がかからないようにダークモード対応を進めました。走行中は日照時間に合わせて自動的にライトモードとダークモードを切り替える機能をリリースしています。
また、マップ画面に表示されるポートピンが色覚特性によって判別しづらいというご意見をいただいていました。
そのため、色でなくてもポートピンの識別ができるようにしたり、色を調整してより多くの方に見やすいマップ画面となるよう改善を重ねています。
引き続きマップ画面の改善を続けていきます。
ライトモードとダークモードのマップ画面の比較
iOSとの統合について
最近のアップデートではiOSとの統合も進めることができました。
具体的には、LiveActivityを利用した走行中の表示、ウィジェットからクイックライド機能などの追加です。
LiveActivityについては、ブログ記事にもまとめていますので、興味のある方はこちらもお読みください[3]。
iOSの機能を積極的に使い、よりよい体験を作ることにこれからも挑戦していきます。
LiveActivity
クイックライドウィジェット
スマートフォンの機能を活かした機能の追加
より気持ちよくライドを楽しんでもらうために、スマートフォンの機能を活かした大きな機能を追加できました。
ひとつめはナビ機能です[4]。
株式会社ナビタイムジャパン様と連携し、出発ポートと返却ポートを選択した際に推奨ルートをアプリ上に表示する機能を追加しました。
試験的な取り組みであるため、都内のライドのみ・iOS端末のみという制限はありますが、交通規制や道幅など自転車特有の情報を考慮したルートを表示します。
安全で快適なルートで移動できるように、今後も改善を続けていきます。
ふたつめは、オートスキャンという機能です[5]。
Bluetoothを利用して近くにある車両を検知し、従来の予約した車両のQRコード読み取りや車両番号の入力の手間を省く機能です。
車両を予約すれば、ポートに到着してLUUPのアプリを開くだけで車両を自動検知し、ライド開始手続きに進めるのでLUUPの乗車体験がとてもスムーズなものになりました。
直近の取り組みとして、どちらもインパクトのある機能追加ができたと思っています。
今後も、スマートフォンの機能を活かしたサービス改善を進めます。
ナビ機能とオートスキャン
今後の課題とまとめ
直近はSwift6が迫ってきているので、この対応を進めたいと思っています。
モジュール分割がよく進んでいるので、モジュール単位で作業を進められたらよさそうだと考えています。
iOSとの統合は意識的に進めていきたいと考えています。例えば、Apple Intelligenceが発表されたので、Intentによるアプリの情報提供や操作手段を増やしておくのは重要だと思います。
今はまだ対応機種が限られるので様子を見て進めていきたいと思っています。
1年半で様々な部分に変化がありました。
リファクタリングは一気にまとめてやれるものではなく、機能開発を進めながらも並行して少しずつコード品質を改善していくものだと考えています。
今後も良い状態を維持していきたいです。
この記事を読んでLuupでのiOSアプリ開発に興味を持っていただけたら、弊社の採用ページもご覧ください。
おまけ
突然ですが、LuupはiOSDC Japan 2024に協賛しています。
Luupからはエンジニア3名が参加する予定です。
当日会場でお会いできたら、iOS開発についてお話ししましょう。よろしくお願いします。
iOSDCトークンは「#街じゅうを駅前化」!
Discussion