見出し画像

iOSアプリ開発のXcodeプロジェクト設定と仲良くなろう

🎄この記事はNAVITIME JAPAN Advent Calendar 2023の11日目の記事です。


こんにちは、かっしかしです。
ナビタイムジャパンでトラックカーナビのiOSアプリ開発を担当しています。
最近iOSアプリ開発を始めた人や、Xcodeのプロジェクト設定に見慣れてない人に読んでもらえると嬉しいです。
この記事はXcode15.0を元に作成しました。iOSアプリのプロジェクト設定を主に説明しています。

Xcodeのプロジェクト設定を開いてもわからない項目が多くてスルーしてしまったことはないでしょうか?また、エラーの内容を調べてプロジェクト設定を変更する必要が出てきた時、変更したらどうなってしまうのか不安になったりしたことはありませんか?

プロジェクト設定の項目を変更するとどのように作用するのかを知っておくことで、もし変更が必要になった際にも何をやっているのかを理解しながら進めることができます。また、ビルド実行時間を短縮したいと思った際に、自分のプロジェクトの設定がどのようになっているのかを知ることで、改善点を考えることができます。

Xcodeでサンプルプロジェクトを作成してみよう

手元のXcodeを開いて、新規のプロジェクトを作成しながら読み進めるとより理解が深まるかと思います。プロジェクトの作成方法は下記です。

  1. Xcodeを開いて、File > New > Project…を選択

  2. Project templateが表示されるので、iOSタブのAppを選択

  3. Product Nameにプロジェクト名を入力

  4. 保存先を選択するとプロジェクトが作成される

プロジェクト設定からターゲットを追加してみよう

左側のペインからProject Navigator(一番左側)を開き、青色のプロジェクトアイコンを選択することで、プロジェクト設定を開きます。
Xcodeのプロジェクト設定は.xcodeprojファイルの記述がGUIで表現されています。プロジェクト設定を表示するとプロジェクトとターゲットの一覧が表示されます。
下はサンプルプロジェクトにいくつかターゲットを追加してみた図です。

プロジェクト設定の表示

試しに新しくターゲットを追加してみましょう。ここではUnit Test用のターゲットを追加します。他にもFramework, Widget Extensionなど色々と選択肢が表示されます。

  1. TARGETSリストの下部の+ボタンでTarget templateを表示

  2. Unit Testing Bundleを選択

  3. Product Nameでターゲットの名前を入力

ターゲット、スキーム、コンフィギュレーションについて

先ほど作成したターゲットには、テンプレートリストに表示されるように、多数の種類があることがわかりました。それぞれ異なる種類の成果物を作成するターゲットがあるのです。ターゲットでビルド対象となるファイルやライブラリなどのインプットと、ビルドの成果物のアプトプットが設定されています。
コンフィギュレーションは、各種ビルド設定のまとまりです。デフォルトで開発時に使用するDebugと本番用に使用するReleaseが用意されています。
スキームは各アクション(Run, Test, Profile, Analyze, Archive)でどのターゲットをビルド対象とするか選択し、各アクションで利用するコンフィギュレーションを設定するものです。例えば、アプリターゲットのコンフィギュレーションを変更することで、Debugで検証用のアプリを作成するスキーム、Releaseで本番用のアプリを作成するスキームなどを用意できます。

実際にサンプルプロジェクトのスキーム設定を確認してみると、ビルドしたいターゲットとコンフィギュレーションが設定されていることが確認できます。

  1. Xcodeエディターの上部のスキームをクリック

  2. Edit Scheme…を選択

  3. Buildを選択するとビルド対象のターゲットを確認できる

  4. Runを選択するとBuild Configurationの項目にて設定されているコンフィギュレーションが確認できる

スキームに設定されているターゲットを確認(SampleAppTarget)
スキームに設定されているコンフィギュレーションを確認(Debug)

ターゲットの設定画面を開こう

ターゲットの設定画面は複数のタブで構成されています。それぞれのタブについて簡単に説明します。

Xcodeのターゲット設定のタブ
  • General

    • サポート端末、OSバージョン、表示名称、バージョン設定、依存設定など様々な設定を行うことができます

  • Signing & Capabilities

    • 主に証明書関連の設定を行います。Appleが提供するサービス(CloudKit、Game Center、アプリ内購入など)の利用設定もこちらから行うことができます

  • Resource Tags

    • On-Demand Resources機能のタグ文字列を管理することができます。容量の大きなリソースをアプリに同梱せず後からダウンロードできるようにする仕組みです

  • Info

    • Info.plistを確認することなどができます

  • Build Settings

    • ビルド設定を一覧できます。コンフィギュレーションごとに設定を行うことができます。後で詳しく見てみましょう

  • Build Phases

    • ビルド実行時に行う処理を記載することができます。こちらも後で設定を追加してみましょう

  • Build Rules

    • 独自の拡張子のファイルをビルドする際の設定を行うことができます

この中でBuild SettingsとBuild Phasesについて詳しくみていきます。Build Settingsの項目数は多数あり、スルーしがちなタブ項目だと思うので、一度じっくり眺めてみましょう。
また、Build Phasesでは色々な設定が可能な一方、自分の感覚ではメンテナンスが行き届かないで不要な設定が残ってしまっていることが多い場所です。定期的に見直すと改善ポイントの発見があるかもしれません。

Build Settingsの一覧を眺めよう

プロジェクト設定のBuild Settingsからターゲットのビルド設定を変更することができます。
こちらからビルド設定の一覧が確認できます。

https://developer.apple.com/documentation/xcode/build-settings-reference

Xcodeからも、右側ペインのQuick Helpにて、それぞれのビルド設定の説明を表示することもできます。
設定項目はたくさんあって、全ては把握しきれません。私が触った経験のある設定項目について紹介します。

  • Architectures

    • 成果物のCPUアーキテクチャの種類を設定できます。arm64, x86_64などが選択できます

  • Debug Information Format

    • クラッシュログを解析する際に必要なdSYMファイルを生成するかどうかを設定できます。Debugコンフィギュレーションのデフォルト設定では生成しないようになっています

    • 私のプロジェクトではDebugでも不要なdSYMを生成する設定になっておりビルド時間が無駄になっていました

  • Swift Compiler Compilation Mode

    • Swiftファイルを再度ビルドする際の設定です。Incrementalにすると変更されたファイルのみがビルドされます。Whole Moduleは常に全てのファイルをビルド対象とします。

    • 普段の開発ではIncrementalに設定しておくことで、開発時のビルド時間を短縮できます

  • Swift Compiler Other Swift Flags

    • Swiftコンパイラが認識できるフラグを設定することができます

    • コード内で検証用のコードとリリース用の処理を分岐したいときに、ここで設定したフラグを参照しています

    • 現在はActive Compilation Conditionsを使う方が良さそうです(参考

Build Phasesで好きなスクリプトを実行してみよう

Build Phasesにて、ビルド実行時に行うタスクを記載することができます。また、コンパイル対象のソースファイルや、依存するターゲットやフレームワーク、アプリに必要なリソースファイルをコピーする処理などもここに記載されています。
そして、自分で好きなスクリプトを追加することが可能です。試しに下記のように設定をしてみましょう。

  1. Build Phases上部の+ボタンを押してNew Run Script Phaseを選択

  2. 最下部にRun Scriptという名前で項目が追加されるので、名前をクリックしてHello Script1に変更

  3. 項目はドラッグすることができるので、Compile Sourcesの前に移動

  4. 続いてもう一つScript Phaseを追加し、Hello Script2として設定

  5. Shellを記載するテキストボックスに下記を記載

Hello Script1の項目に下記を記載
echo "Hello🚚"

Hello Script2の項目に下記を記載
echo "Hello🚚🚚"
Build Phasesにてshell scriptを設定

そして、ビルドを実行します。すると、ビルドログは下図のような結果になりました。ビルドログは左側ペインの一番右側にある項目から確認することができます。
Hello Script1が実行された後、アプリターゲットのSwiftファイルのコンパイルが行われ、Hello Script2が実行されています。Build Phasesで設定した処理が上から順に実行されています。

期待通りの順序で処理が実行された

ここで、私が実際に行っているBuild Phasesの処理を紹介します。皆さんのプロジェクトでもどのような処理が行われているのか確認してみてください。
R.swiftの実行
R.swiftによってリソースファイルを扱うようにしています。R.swiftはBuild Tool PluginによってSwift Package Managerで実行形式が取得できるのですが、XcodeCloudで実行するとエラーが発生してしまいます。(参考)
そこでshell実行でR.swift.generatedのファイル生成処理を行うようにしています。
SwiftLintの実行
Lintをビルド時に実行することで、早く修正点を開発者に知らせることができます。後述するのですが、対象ファイルを絞ることで実行時間を短縮するようにしています。

発展編:ビルド時間を短縮できるところを探そう

Intel Macで開発をしていた時代から比べると、Apple SiliconのMacでかなりビルド時間は早くなりましたが、まだまだ改善の余地はあるかもしれません。調査する方法を紹介します。

ビルドログを眺めると何か発見があるかもしれません。ビルドログでは、ビルドの開始から終了までのログが文字列で出力されます。また、よりグラフィカルにタイムラインを表示する機能がXcode14にて搭載されました。下図ではオレンジ色の部分が外部パッケージのコンパイル処理となっており、大部分を占めていることがわかります。また、コンパイルは並列に実行されるので複数のスレッドで表示されています。

  1. ビルドログを左側ペインの一番右側にある項目から表示

  2. 右端のビュー表示切り替えボタンからAssistant ビューを表示(もしくはcontrol + option + command + enter)

  3. ビルドログの特定の行を選択するとその行のタイムラインがフォーカスされる

下部にタイムライン表示が行われている

ビルド時間短縮について考えてみよう

x86とarm64のビルド時間の差
ビルド設定のArchitecturesの項目と関連する内容です。
私のプロジェクトでは、XCFrameworkに対応していない社内ライブラリをM1 Macで使用する際にiPhone端末用のアプリビルドはarm64で行い、シミュレータ用のアプリビルドはx86で行うという設定を行いました。この時のx86向けのビルド時間が遅いことがわかりました。x86向けのビルドは内部的にRosseta2によって行われていると思いますが、その分だけオーバーヘッドがあるのかなと思います。

試しにサンプルプロジェクトにてArchitecturesの項目を変更してビルドを行ったところ、1秒ほどの差がありました。これはファイル数に応じて変化が大きくなるため、大きなプロジェクトほど影響を受けます。
XCFrameworkを使用することにより、シミュレータ用のアプリビルドでもarm64アーキテクチャでビルド実行ができるようになります。現在社内向けライブラリをXCFrameworkでビルドできるように対応中なので、今後は解決できると思います。

SwiftLintの対象を変更のあったファイルのみにする
ビルドログを見ていると、SwiftLintがコンパイル後に毎回実行されていることに気がつきました。SwiftLintの実行対象ファイルがプロジェクトのソース全体になっていたため、変更されていないファイルもLintの対象になっていたのです。そこでこちらの情報を参考にして、変更を行なったファイルのみをLintの対象にするようにしました。
手元のプロジェクトではLint処理に1.1秒かかっていた処理が0.7秒に短縮されました。この時間は対象ファイル数に応じて変動します。

Firebase CrashlyticsのdSYMアップロードの処理
公式ドキュメントではBuild PhasesにてdSYMファイルをアップロードするスクリプトを追加するようになっています。しかし、開発中にはCrashlyticsでクラッシュを解析する必要がないので、この処理は不要と考えました。
Build Phasesで実行することをやめて、代わりにCIにてアプリを作成するスクリプトにdSYMアップロード処理を追加しました。手元ではこのアップロード処理に0.1秒かかっていましたので、この分だけビルド時間を短縮できました。

コア数によるビルド並列化の影響
Macを新しくすればCPUの進化により、ビルドは高速化すると思いますが、CPUのコア数にも注目してみたいです。タイムライン表示にて確認したように、Xcodeは並列ビルドを行います。これはMacのコア数分並列実行されるため、コア数が多いMacならビルド時間への効果も高いことになります。Mac買い替えの際にはこちらも意識してみるとよいかもしれません。

いくつか紹介したビルド短縮の対応ですが、これらの対応は1回あたりの短縮時間はわずかでした。しかし、開発を続けていきビルドを何度も繰り返していくと大きな差になると思います。
そのほか改善点、気をつける点についてAppleがドキュメントを作成しています。


今回は、サンプルプロジェクトのプロジェクト設定を触ってみたり、実際にビルド時間を短縮する方法について紹介しました。この機会に、作業しているプロジェクト設定を見直してみてはいかがでしょうか。
Xcodeにより親しみを持って、皆さんのプロジェクトがより良いものになれば幸いです。