iOS Swift SwiftUI mobile

minne iOS 2024年の開発環境

iOS Swift SwiftUI mobile

minne事業部モバイルチームのエンジニアをしております、@umatoshiです。最近はパルワールドに夢中で、全力で配合に取り組んでいます。 好きなパッシブスキルは脳筋です。

現在私は、minneのiOSアプリを開発しており、最新バージョンのXcode(執筆時点では15.2)対応とiOS15サポート終了予定等、2021年当時の記事からアップデートしたので、採用している新技術や開発効率を上げるための工夫をはじめとしたトピックを、minneのiOSチームに興味がある方へ向けて共有したいと思います。

去年はiOSDC 2023でブース出展もしていて、弊社から登壇もしていてminneについて紹介しています。こちらもどうぞ。

概要

まずiOS 14のサポートを終了しており、SwiftUI、 GraphQL、 SPM(Swift Package Manager)、 Combine、 Swift Concurrencyなど、チームで多くの面白い新技術を活用して開発効率を上げています。

全社的にGitHub Copilotを導入しており、特にXcode環境ではCopilot For Xcode を通じて、コード生成AIを最大限に活用しています。 GitHub Copilotの導入経緯は、ペパボでのGitHub Copilotの導入によってペパボの開発生産性はどう変化したのかをご参照ください。

ここからはより詳しい内容を説明します。

minne iOSアプリの歴史と概要

まず、minneのiOSアプリは2012年から開発が始まって10年以上の歴史があります。当時は全てObjective-Cで書かれていましたが、現在ではソースコードの99%以上がSwiftに移行できています。 現在ではObjective-Cを触る機会は滅多にありません。今年中には100% Swift化を目指しています。

アーキテクチャーは、MVVM+Rを採用しており、今後はSwiftUIがメインになる事を考慮した形にしています。 ビルドは最新Xcode上でビルドできるようにしていてプロジェクトはXcodeGenを使用し、CocoaPodsとSPMの両方を使ってサードパーティ製のライブラリを導入しています。 基本的にはSPMを推奨していますが、止むを得ない場合はCocoaPodsを使用していいます。CocoaPodsについては徐々にその割合を減らし、今年中にCocoaPodsを止める予定で進めています。

CI/CDはBitrisefastlaneTestFlight を使っています。 ビルドやデバッグに関する処理については、できるだけfastlaneで完結する方針を取っています。BitriseをはじめとするCIサービス設定を少なくする事でサービスへの依存度を下げ、容易に手元での検証ができる体制を作っています。

開発体制

minneは、iOSエンジニア3名で開発しており、デザイナー、ディレクター、ほかのエンジニアと協力して、機能追加と保守を行っています。コミュニケーションは主にSlack、 GitHub Enterprise、 Google Meetで行っています。

2週間ごとのサイクルでアプリをリリースする計画を立てています。 また、隔週でminne事業部の目標達成に向けた中期的な(半年〜1年先の)モバイルアプリ戦略の会議を、iOS・Androidのチーム合同で開催しています。会議では課題の共有やリスクの低減、進捗状況の確認を行っています。

チームの特徴としては毎朝同期的なレビューする時間をもうけてコミュニケーションをとって「速(そく)」を出しています。

ビルド環境

iOSアプリのビルドはマシンパワーを必要とするのでApple Siliconが載った高性能なMacBookも支給されています。 現在、私はMacBook Pro M1 Pro 32GBを使用していますが、3月からMacBook Pro M3 Pro 36GBに乗り換え予定です。💻

Project

基本のバージョン管理ツールは定番のGitとGitHub Enterpriseです。

ビルドは最新Xcode上でビルドできるようにしていてプロジェクトはXcodeGenを使用し、CocoaPodsとSPMの両方を使ってサードパーティ製のライブラリを導入しています。

全社でGitHub Copilotを導入しているので Copilot For Xcode とXcode上でコード生成AIをフル活用しています。 SwiftコーディングガイドとしてはSwiftLintSwiftFormatを使用しており、gitコミット時に差分ファイルのみ実行するような工夫を入れています。

また、Pull Requestの作成やコメント時に動作する、OpenAI GPT-4を活用した内製AIコードレビューツールを導入しました。これにより、コードの差分を分析し、要約やリファクタリングの提案をAIが自動で行います。

このような仕組みを導入することでレビュー時には人間が本当に必要なところを集中的にレビューすることができています。

開発言語と主要フレームワーク

99%以上Swiftです。Objective-Cのファイルはあと残り8ファイルなので今年中に100% Swiftにします! 新規のものは必ずSwiftで開発していて、残っているObjective-Cは少しずつ減らしています。

最新バージョンのXcode 15.2への対応は既に完了しており、Xcode 15.3 betaへの対応も確認できています。近日中にiOS 15のサポートを終了する事もあり、現在も継続してiOS16以降を見据え、SwiftUIでの実装がしやすくなる環境を整備しています。 また、6月にXcode 16 beta、iOS 18 beta が来る想定なのでいち早く対応できるように取り組んでいます。

UIについてですが、ほぼUIKitの実装が多いですが新規画面はSwiftUIを採用していて、UIKitからSwiftUIに置き換えて行っています。 まだまだUiKitの画面が多いのでSwiftUIにたくさん移行していきたいです。

また、UIKitはモダンなAPIを使うように心がけており、例えば、UICollectionViewを使っている新規画面でだいたいDiffableDataSourcesCompositionalLayoutを使っています。

ログ処理に関しては、Google Analytics for FirebaseReproPuree-Swiftでラップし、統一されたインターフェースを実現しています。

アーキテクチャ

2012年に開発が始まった当時は100% Objective-C製で、EntityクラスとViewControllerクラスに加え、ネットワーク通信処理やデータ永続化処理等をまとめたManagerクラスで書かれた、典型的な「Fat ViewController」様な形のアーキテクチャとなっていました。

しかし、機能の増加と共に様々な課題が顕在化したため、2017年頃からチームはアーキテクチャの見直しを始め、GUI設計パターンとしてModel View Presenter(MVP)を採用しました。特に、画面遷移やモデル層のアーキテクチャに関しては、明確な方針が定められていませんでした。

2019年にSwiftUIとCombineが発表され、また、minneが90%以上Swift製になりました。SwiftUIでは、明示的に画面更新を行えず、また、Combineの依存によりFunctional Reactive Programming(FRP)的なアプローチが求められ、MVPのままだと、SwiftUIへ移行しにくいのでは?という疑問がありました。

そのため、MVVMにすべきか、また、どういうふうに実装するかについて調査・議論し、その結果、新しい画面はできるだけMVVMで作り、数多く残っているFat ViewControllerをMVVMにすることが決まりました。(MVPになっている画面はいったんそのまま)

そのあと、2020年から画面遷移やモデル層の設計決めを本格化し、モデル層について議論してきました。MVVM、 VIPER、 Redux、 The Composable Architecture (TCA)、 Fluxをそれぞれ比較して、TCAをいくつかの画面で実際に試し、導入した場合を検証しました。実際TCAをプロダクションに使ってみて、他の設計と比較しました。ほかの設計とは、MVVMのGUI設計に、モデル層の定義してみたアーキテクチャのことです。

以下の理由で、最終的にTCAではなく、MVVM + Repository層というアーキテクチャに決まりました。

  • プッシュ通知などの起動経路が非常に多いので、ビュー階層を一新しない限り、AppStore、 AppReducer、 AppEnvironmentが膨大になってしまう可能性がある
  • フレームワークを使うことになるので、TCAの実装の細部まで知る必要がある
  • テストやビューコード、ビジネスロジックがかなり外部ライブラリに依存してしまう
  • 一気にTCAに乗り換えることができず、MVP/MVVM/TCA混在になる併用期間が長そう
  • 画面間の情報共有はまだ考えることがある

現在採用しているアーキテクチャに関する詳細はこちらでも紹介しています。

過去にminneではRxSwiftを使用していましたが、完全に取り除きました。その代わりにAppleが提供しているCombineを利用してFRPの実装を書いています。SwiftUIとCombineの親和性が高いので、SwiftUI時代を見据えて少しずつ移行をしています。

Swift 6に向けてSwift Concurrency対応などを徐々に進めています。

ネットワーク通信

minneではREST APIの通信部分にはAlamofireをやめて通信ライブラリや通信のモックライブラリ(OHTTPStubsなど)を一切使っていません。代わりにFoundationの素のURLSessionをラップしたAPIクライアントを実装しています。

レイヤーが減るので、デバッグもしやすくなる、アップデート対応がなくなる、依存性が減るのような理由からです。

ただ、REST APIからGraphQLに移行しているので、その辺りの通信処理を一部apollo-iosに託しています。 GraphQLの処理はApolloに依存するので、内部ではREST APIクライアント・GraphQLクライアントを意識せずに呼び出す事を可能にするネットワークレイヤーを設計しています。マッピングはデータ取得時にGraphQLのEntityからアプリ内部で利用している既存Entityへ変換して利用しています。

Test

社内ではRuby on Rails経験者が多い事もあり、RSpecに近く易しい書き方が可能なQuickNimbleを利用しています。特にViewModelのテストにおいては、QuickとNimbleを活用してTDDを意識した形で書く事ができています

依存性注入(DI)はライブラリを利用せず、便利なProtocol Composition (Using protocol compositon for dependency injection)で紹介されている形を参考にして、オーソドックスなConstructor Injectionをする方針を取っています。 Mockは型付けの強いSwiftでは作るのが面倒ですが、SourceryのAutoMockableテンプレートを活用して、Protocolから生成しています。

CombineのコードはXCTestExpectationだけでは、以下の問題があるので、CombineExpectationsCombineSchedulersを使ってテストを書いています。

  1. debounceなどのオペレーターでschedulerを注入しないと、テストが遅くなってしまう
  2. 記述量が多い

CI/CD

ツールとしてfastlane + Dangerを積極的に活用しており、サービスとしてBitrise + TestFlightを使っています。加えてGitHub Actionsについてもスペルチェックやラベル付け等といった補助的な形で利用しています。 方針として、できるだけ多くのことを自動化することによって、開発とレビューに割ける時間を最大化し、アウトプットの量を増やし品質を向上できると考えています。

Fastlaneはもやはモバイル開発の定番の自動化ツールで、スクリーンショット作成以外は、メインのアクションをすべて活用しています。

  • アプリのベータと本番用ビルドを配信
  • App Storeメタデータを管理
  • プッシュ通知証明書やProvisioning Profileの更新
  • CI上のテスト実行

また、OpenAI GPT4を利用した内製AIコードレビューのツールを導入しています。私ごとですが優秀でかなりおすすめです。

技術課題

minneのiOSアプリは今年で12年目となる程に歴史が長く、全ての画面を合計すると120個以上あるため、ビルド時間が長く、そして古いままの画面ではFat ViewControllerが残っている状態です。 ビルド時間の問題の解決については、基本的には、Module分割、Fat ViewControllerの画面をSwiftUI+MVVM+RもしくはUIKit+MVVM+Rへの書き換えに積極的に取り組んでいますが、まだまだ追いついていないのが現状です。

Apple SiliconのハイスペックなMacBook Proで気にならないぐらいには解決していますがメインバンドルが大きいのでSwiftUIのプレビューが動かないなど改善が必要です。

そしてやらねば案件としてSwift 6に向けてSwift Concurrency対応、4月からApp Storeの審査要件に加わるプライバシーマニフェスト対応、CocoaPodsを排除してSPMに一本化、REST APIからGraphQLへ全面移行など課題がいっぱいあります。

まとめ

以上が、minne iOSアプリでの開発状況です。モダンな環境でSwiftUI、Combine、Swift Concurrency、GraphQLを積極的に増やして、新機能も定期的に追加することにご興味ある方は、ぜひ応募してください! また、質問や疑問などあれば、ぜひカジュアル面談にお申し込みくださいませ〜。