Xcodeでプロジェクト間共通のマクロを定義する

SDKなどの複数のライブラリやプログラムで構成されるモジュールを開発しているときなど、複数のプロジェクト間で共通のマクロ定義を参照することがあります。

例えば、ある特定の機器と通信するSDKを開発しているときに、対象となる機器によって定数を切り替える必要があるとします。どの機器用にビルドするのかということをヘッダファイルのマクロで切り替えられるようになっているとします。

このようなとき、共通のヘッダファイルをインクルードしているということであれば、そこで定義します。マクロを定義します。例えば、Configuration.hファイルなどを用意するなどです。しかし、複数のプロジェクトファイルで構成され、そのような共通ファイルを置くことも難しいというケースではどうでしょうか?例えば、Swiftであればヘッダファイルは使用しないですね。

このような場合にはビルド設定ファイルを使用します。記事を見ながらコードを実行してみたい方は、次のリンクからサンプルファイルをダウンロードしてください。

サンプルファイル (Modules.zip)

スポンサーリンク

ビルド設定ファイルを作成する

ビルド設定ファイルの作成方法とプロジェクトへの設定方法については、以下の記事を参照してください。

関連記事

Xcodeで設定するビルド設定はプロジェクトファイル(*.xcodeproj)に保存されます。通常のアプリ開発ではこれだけで十分なのですが、中規模以上の開発を行っているときや特殊なプログラムを作っているときなどは、もう少し柔軟に制御したい[…]

ここでは、例として次のようなディレクトリ構成で3つのプロジェクトを作成しました。

Modules
├── Common.xcconfig
├── Module1
│   ├── Module1
│   │   ├── Debug.xcconfig
│   │   ├── Release.xcconfig
│   │   └── main.swift
│   └── Module1.xcodeproj
├── Module2
│   ├── Module2
│   │   ├── Debug.xcconfig
│   │   ├── Release.xcconfig
│   │   └── main.m
│   └── Module2.xcodeproj
└── Module3
    ├── Module3
    │   ├── Debug.xcconfig
    │   ├── Release.xcconfig
    │   └── main.cpp
    └── Module3.xcodeproj

Module1Module2Module3はそれぞれ別々のプロジェクトファイルによりビルドされるプログラムになっており、お互いは参照していません。各プロジェクトはDebug.xcconfigRelease.xcconfigというビルド設定ファイルを持っています。名前の通り、デバッグ用とリリース用です。Debug.xcconfigRelease.xcconfigは次のように書かれており、Modules/Common.xcconfigを参照するようになっています。

#include "../Common.xcconfig"

これにより、Modules/Common.xcconfigファイルを編集することで、Module1.xcodeprojModule2.xcodeprojModule3.xcodeprojのビルド設定が変更できるようになります。

Swiftのコード

Swiftはシンボルの定義だけできるようになっています。Xcode上で定義するときは、ビルド設定のSwift Compiler - Custom FlagsOther Swift Flags-Dシンボル名の書式で定義します。複数あるときは、スペース区切りで列挙します。

Other Swift Flags で定義する

ビルド設定ファイルで定義するには

Other Swift Flagsをビルド設定ファイルで定義するときはOTHER_SWIFT_FLAGSを使用します。例えば、スクリーショットと同様にUSE_PHYSICAL_DEVICEを定義するときは次のように記述します。

OTHER_SWIFT_FLAGS = $(inherited) -DUSE_PHYSICAL_DEVICE

ビルド設定ファイルに上記のように記述すると、スクリーンショットのようにXcodeで表示したビルド設定画面にも反映されます。

スポンサーリンク

コードでの分岐方法

Swiftのコードで定義したシンボルによってコードを分岐させるには、#if文を使用します。

#if シンボル

シンボルが定義されているときにビルドされるコード

#else

シンボルが定義されていないときにビルドされるコード

#endif

#if文とif文の大きな違いは、#ifで分岐されて実行されないコードは、バイナリに含まれないということです。つまりコンパイル対象になりません。一方、if文の場合はどちらもコンパイルされバイナリに含まれて実行されないというだけです。

次のコードは、USE_PHYSICAL_DEVICEが定義されているかどうかをチェックしています。

import Foundation

#if USE_PHYSICAL_DEVICE
print("Module1 DeviceType: Physical Device")
#else
print("Module1 DeviceType: Simulator")
#endif

Objective-Cのコード

Objective-CはC言語及びC++と共通の定義を参照します。Xcode上で定義するには、ビルド設定のApple Clang - Custom Compiler FlagsOther C Flags-Dマクロの書式で定義します。-Dマクロ=値で定義した場合は、マクロに値を割り当てます。複数あるときは、スペース区切りで列挙します。

Other C Flagsで定義する

ビルド設定ファイルで定義するには

Other C Flagsをビルド設定ファイルで定義するときはOTHER_CFLAGSを使用します。例えば、スクリーンショットと同様にUSE_PHYSICAL_DEVICEを定義するときは次のように記述します。

OTHER_CFLAGS = $(inherited) -DUSE_PHYSICAL_DEVICE

ビルド設定ファイルに上記のように記述すると、Xcodeのビルド設定画面にも反映されます。

コードでの分岐方法

Objective-Cのコードで、定義したマクロによってコードを分岐させるには、#ifdef#if definedを使用します。

#ifdef マクロ

マクロが定義されているときにビルドされるコード

#else

マクロが定義されていないときにビルドされるコード

#endif

次のコードは、USE_PHYSICAL_DEVICEが定義されているかどうかによって切り替えているコードです。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
#ifdef USE_PHYSICAL_DEVICE
        NSLog(@"Module2 DeviceType: Physical Device");
#else
        NSLog(@"Module2 DeviceType: Simulator");
#endif
    }
    return 0;
}

C言語/C++のコード

C言語とC++の場合は、Objective-Cと同じです。

コードでの分岐方法

コードもObjective-Cと同じです。#ifdef#if definedを使用します。次のコードはUSE_PHYSICAL_DEVICEが定義されているかどうかによって切り替えているコード例です。

#include <iostream>

int main(int argc, const char * argv[]) {
#ifdef USE_PHYSICAL_DEVICE
    std::cout << "Module3 DeviceType: Physical Device" << std::endl;
#else
    std::cout << "Module3 DeviceType: Simulator" << std::endl;
#endif
    return 0;
}

実行結果

ここまでのサンプルコードをCommon.xcconfigファイルを空にして実行すると、次のように出力されます。

Module1 DeviceType: Simulator
2022-07-19 16:03:43.534012+0900 Module2[3808:159692] Module2 DeviceType: Simulator
Module3 DeviceType: Simulator

Common.xcconfigに以下のように記述して、マクロを定義します。

OTHER_SWIFT_FLAGS = $(inherited) -DUSE_PHYSICAL_DEVICE
OTHER_CFLAGS = $(inherited) -DUSE_PHYSICAL_DEVICE

改めてサンプルコードをビルドして実行すると、次のように出力され、複数のプロジェクトのマクロを1つのファイルで定義できていることが確認できます。

Module1 DeviceType: Physical Device
2022-07-19 16:04:36.900064+0900 Module2[3843:162776] Module2 DeviceType: Physical Device
Module3 DeviceType: Physical Device
スポンサーリンク
最新情報をチェックしよう!