はじめに
こんにちは!2024年4月に新卒iOSエンジニアとして入社したDaiseiTanaka, 山本 迅平, developer-9です。
先日、新卒エンジニア研修の一環として、バックエンド、クライアント、インフラ、MLを含むメンバー8人で、3週間のチーム開発を行い、私たちのチームは見事最優秀賞をいただくことができました。
この記事では、クライアントサイドの開発における工夫点やチームの取り組みについて紹介させていただきます。
本研修とチーム目標について
本研修の概要と、我々チームLが立てた目標に関しては下記記事で紹介されているため、ぜひご覧ください⇩
2024年度 開発研修 最優秀チームの取り組みと研修全体概要
クライアントにおける品質とは
私たちのチーム目標は「必須要件に注力し質の高いプロダクトを完成させる」であったため、まずはiOSエンジニアの3人で、クライアントにおける質の高さを話し合って決定し、それを満たす要件で実装を進めるようにしました。必須要件の他に、私たちは質の高さを「開発体制」、「コード品質」、「UX」の観点で分割しました。各観点に対し、質が高い状態を列挙し、網羅的に達成できるようにタスクを分けました。各項目は以下となっており、項目ごとに達成するべき内容をメンバーと話し合って決定し、最終日には全ての項目を達成できました。
例として、「新メンバーを想定したドキュメント整備」では、導入ツールの使用方法やアーキテクチャ図などをGitHubのWikiページにまとめたり、「コーディングフォーマットの標準化」ではSwiftLintを導入したり、「クラッシュしない」では強制アンラップ未使用や破壊的なUIの入力を防止する実装を徹底するようにしました。
形式的なAPIコールを自動生成する
今回の研修では求められている実装要件に対して実装期間がシビアなため、チーム全体の方針として「人間が書かなくてもいいような反復的なコード」は極力自動生成を活用することで開発効率を上げる取り組みを並行して行いました。
このセクションでは導入したツールとそれによって具体的にどう開発体験が変わるのかから入り、最後に導入にあたっての考えるべきことについて解説できればと思います。
早速ですが、クライアントチームでは下記の2点の自動化を試みました。
- APIコールに要するコード
- ユニットテストを書くためのモッククラス
新卒研修では上記2つの目的を解決するために様々なツールを導入しましたが、このセクションでは、「API呼び出しに要するコード」に的を絞り解説に移りたいと思います。
Swift-OpenAPI-Generator
APIコールに要するコードの自動生成を担当するのはSwift-OpenAPI-Generatorというツールです。
サーバーサイドが吐き出してくれるOpenAPIドキュメントからSwiftのAPIクライアントコードを生成するツールです。
技術自体は昨年のWWDC2023で発表されました。
Swift Package plugin として提供されているのでコードの生成から利用を、Xcodeを離れることなくスムーズに行えるようになります。
Swift-OpenAPI-Generatorを導入することで何が変わるか
Swift-OpenAPI-Generatorを使うことのメリットは、クライアントからAPI呼び出しに要する形式的なコードを最小限にすることができるようになります。
結果として、クライアントが実装するコードはRepository層での生成されたメソッドの呼び出しからエラーハンドリングに注力することができます。
また、ビルドごとにサーバーが吐き出してくれたOpenAPIドキュメントからコード生成をローカルのDerivedData内で行うことから、APIドキュメントと擬似的に同期状態にすることも可能です。
そのため生成されたコードをリポジトリに含める必要もなくなるという副次的なメリットがあることもSwift-OpenAPI-Generatorの魅力です。
今回、研修課題にSwift-OpenAPI-Generatorを導入することで以下の向上を期待しました。
- クライアントチーム全体の生産性向上
API呼び出しに関わるコードを自動生成することで、儀式的に行われていたHTTPリクエストの作成やレスポンスの解析、エラーハンドリングの実装など、手間のかかる作業を大幅の削減が見込めます。
先述したようにOpenAPIドキュメントに基づいてコードを自動生成するため、新しいエンドポイントの追加や既存エンドポイントの変更が発生しても、
一貫性のあるコードがビルドごとに自動で生成されます。
この体験により、ヒューマンエラーを減らし、開発者はAPIの具体的な実装よりも、アプリのビジネスロジックやUI/UXの改善に集中できるようになります。
結果的に品質やチームのアジリティが向上が期待できます。
より具体的に、導入することによってコードがどう置き換わるのかを従来のURLSessionと比較してみるだけでも、コード量という観点からも変化が明らかになります。
URLSession
Swift-OpenAPI-Generator
URLSessionではbaseURLにハードコードが必要になったりと保守性の観点からもベストとは言い難いですが、Swift-OpenAPI-GeneratorではAPIドキュメントから生成されたstatic funcを設定してあげることで対応可能です。
// Types.swift
/// Server URLs defined in the OpenAPI document.
package enum Servers {
/// Example service deployment.
package static func server1() throws -> Foundation.URL {
try Foundation.URL(
validatingOpenAPIServerURL: "https://example.com/api",
variables: []
)
}
}
考慮すべきこと
新しい技術をプロジェクトに導入することは必ずしもポジティブな影響ばかりではないこともここで留意しておきたいところです。
ここでは導入にあたって最低限考慮しておきたいことを立項します。
-
- 技術におけるキャッチアップコスト
Swift-OpenAPI-Generatorは2024年現在では比較的新しく、実務への導入例も少ないため情報が乏しく、公式のレポジトリを元に内部実装を読むことが正確に挙動を理解することにつながります。
しかしライブラリの性質上、手元で小さく試してみるという手順が取りにくいためキャッチアップには想定よりも時間を要することがあります。そのため最速で実装が求められている場合はキャッチアップコストがチームで許容できるのかを今一度検討する必要も出てきます。
-
- APIの仕様に対応できるのか
例えば、今回の研修にはそれぞれのリクエストヘッダーにjwtを含めてサーバーに投げる要件が求められていましたが、HTTPリクエストをインターセプトすることは可能なのか、事前の見積もりで解消しておく必要がありそうです。
-
- 損益分岐点の見極め
導入によりもたらす便益とキャッチアップから導入〜運用までのコストの見積もりの誤りが生じると、最終的に損益分岐点を超えることなく(つまり導入によるメリットがデメリットを下回る状態のまま)開発が終了することになり兼ねないこともここで警鐘を鳴らしておきたいところです。
先述した通り、実装までの障壁が決して低くはないかつ、綿密な事前見積もりとクライアント、サーバーサイド間の連携が必要になります。
先般行われたCA.swift#20で運用に際して苦戦したところや細かいtipsについて解説しているので、今回の経験が第三者のサポートになれれば幸いです。
API仕様の整合性を担保する
SwiftOpenAPIGeneratorはビルド時に、APIの仕様を記したopenapi.jsonファイルを読み込み自動でAPIクライアントのコードを生成します。今回、クライアントとバックエンドはそれぞれ別リポジトリを利用していたため、バックエンド側でAPIの仕様変更があった場合、手作業でクライアント側のリポジトリのopenapi.jsonを更新する必要がありました。これにより、伝え忘れや編集ミスによって、バックエンド側のAPI仕様との齟齬によって予期せぬエラーが発生するリスクがありました。
これらのリスクに対し、自動でバックエンド側の最新のopenapi.jsonを取得する仕組みをつくりました。
xcodegenを用いてプロジェクトファイルを管理していたことから、openapi.jsonを取得するタイミングをプロジェクトファイルを生成するタイミングとしました。これにより、プロジェクトファイル生成後のビルドのタイミングで最新のAPIクライアントコードが利用できるようになります。
また、xcodegenなどのコマンドはmakefileを使って管理していました。update-apijsonコマンドとして、最新のopenapi.jsonをクローンしてコピーするコマンドを用意し、プロジェクトファイルを生成するxcodegenコマンドの依存関係にupdate-apijsonを追加しました。
xcodegen: update-apijson
@mint run yonaskolb/XcodeGen xcodegen generate --use-cache
make open
update-apijson: ## 最新のapi.jsonを取得
# backendのリポジトリをクローン
git clone git@github.com:backendRepository.git ./tmp
# 最新のopenapi.jsonをコピー
cp -rf ./tmp/docs/openapi.json ./l_client-iOS/openapi.json
rm -rf ./tmp
これにより、以下のように`make xcodegen`を実行することで、プロジェクトファイル生成後、自動でopenapi.jsonを更新でき、常に最新状態で開発を進められるようになりました。
make xcodegen
タスク・Document・進捗管理
我々のチームはタスク管理に、GitHubのプロジェクト機能を活用することで、タスク管理が大幅に効率化されました。特に効果が大きかったのは、タスクから直接Issueを作成し、その進捗を紐づけることが可能な点です。これはIssueを用いて施策を管理するチームにとって大きな利点となります。PRにissue番号を記載し、紐づけているプロジェクトも多いでしょう。さらに、Issue作成時にテンプレートを設定しておくことで、必要なフォーマットをIssue作成時に自動で生成することができます。
GitHubのプロジェクトでは、各メンバーの進捗状況や担当タスクの量を、特別なカスタマイズなしで確認することができます。また、各タスクに優先度や工数(タスクの重さ)のタグを付けることで、タスクの分類も可能になります。
ドキュメント管理についても、GitHubのWikiを利用することで、マークダウン形式で記述することができます。他サービスと比べると機能が充実しているわけではないので、必ずしもGitHubのWikiを利用する必要はありませんが、ドキュメントもGitHub上で一元管理できれば、情報が他のサービスに分散することなく、一貫した管理が可能となります。特にリッチなフォーマットを利用したい理由がなければGitHubのWikiを利用するのは選択肢に入るのではないでしょうか。
まとめ
ここまで読んでいただき、ありがとうございました!
三週間という短い期間でしたが、「品質」という目標に絞り、その目標を達成するために必要な技術を学び、最後には最優秀賞を取ることができた経験は今後のエンジニア人生にとって非常に価値あるものになりました。
本研修を通して学んだことを活かし、配属先でもチームを引っ張っていけるようなエンジニアに成長できるように頑張っていきます!