Define common macros across Xcode projects

When you develop a module consisting of multiple libraries and programs such as SDK, you can define the common macros referenced by multiple projects.

For example, when you develop the SDK for communicating with a specific device, suppose the constants are changed for each target device. Then, you could switch the target device with the common macros in the header file.

In this case, you should define the macro if the project’s source files include the common header files. For example, create the Configuration.h file. But how about a case with multiple projects where it is hard to put the common header file? For example, Swift doesn’t use the header file.

You can use the build configuration settings file in this case. If you want to try it on your machine while reading this article, you can download the sample project from the following link.

TOC

Create the configuration settings file

See the following article for information about creating and configuring the configuration settings file.

For example, we have created three projects with the following directory structure.

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

Module1, Module2, and Module3 are programs built by separate project files and do not reference each other. Each project has two build configuration files, Debug.xcconfig and Release.xcconfig. Debug.xcconfig and Release.xcconfing are written as follows and include Modules/Common.xcconfig.

#include "../Common.xcconfig"

By this, you can change the build configuration settings of Module1.xcodeproj, Modue2.xcodeproj, and Module3.xcodeproj by editing Modules/Common.xcconfig.

Code in Swift

Swift can define symbols but not symbols’ value. To define with Xcode, write in the form -DSYMBOL at Other Swift Flags of Swift Compiler - Custom Flags in the build settings. Spaces separate multiple symbols.

Other Swift Flags
Other Swift Flags

Define in the configuration settings file

You can configure the Other Swift Flags in the configuration settings file by OTHER_SWIFT_FLAGS. For example, write the following code in the configuration settings file. In that case, the USE_PHYSICAL_DEVICE symbol is defined the same as in the above screenshot.

OTHER_SWIFT_FLAGS = $(inherited) -DUSE_PHYSICAL_DEVICE

The configuration settings files are also reflected in what is displayed in Xcode.

Branch by the symbol in the code

To branch by the symbol in the Swift code, you can use #if statement.

#if symbol

The code will be executed when the symbol is defined.

#else

The code will be executed when the symbol is not defined.

#endif

The primary difference between the #if and the if statements lies in whether the binary includes the code. Code branched by the #if statement but not executed will not be compiled into the binary. On the other hand, code branched by the if statement will be compiled, even if it is not executed. Thus, this non-executed code still exists within the binary.

The following code checks whether the USE_PHYSICAL_DEVICE symbol is defined.

import Foundation

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

Code in Objective-C

The symbols used in Objective-C are the same as C/C++. You can define them in the Xcode by writing in the form -DMacro at Other C Flags of Apple Clang - Custom Compiler Flags in the build settings. Also, writing in the form -DMacro=Value assigns the Value to Macro. Spaces separate multiple macros.

Other C Flags
Other C Flags

Define in the configuration settings file

You can configure the Other C Flags in the configuration settings file by OTHER_CFLAGS. For example, writing as follows defines USE_PHYSICAL_DEVICE as the above screenshot.

OTHER_CFLAGS = $(inherited) -DUSE_PHYSICAL_DEVICE

The configuration settings file will also be reflected in the Xcode build settings screen.

Branches in the code

#ifdef or #if defined can branch the code with macros in Objective-C code.

#ifdef Macro

The code will be executed when the macro is defined.

#else

The code will be executed when the macro is not defined.

#endif

The following code branches by whether the USE_PHYSICAL_DEVICE is defined.

#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;
}

Code in C/C++

The code in C/C++ as the same as in the Objective-C.

Branches in the code

#ifdef and #if defined branches with macro in C/C++ too. The following code branches with whether the USE_PHYSICAL_DEVICE is defined.

#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;
}

Execution Result

Run the above sample codes with an empty Common.xcconfig file then the execution results to be the following.

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

Write the following code into Common.xcconfig.

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

Then run sample code and the program outputs as follows, and you can see that macro in a file are used by multiple projects.

Module1 DeviceType: Physical Device
2022-07-19 16:04:36.900064+0900 Module2[3843:162776] Module2 DeviceType: Physical Device
Module3 DeviceType: Physical Device

Authored Books

Let's share this post !

Author of this article

Akira Hayashi (林 晃)のアバター Akira Hayashi (林 晃) Representative(代表), Software Engineer(ソフトウェアエンジニア)

アールケー開発代表。Appleプラットフォーム向けの開発を専門としているソフトウェアエンジニア。ソフトウェアの受託開発、技術書執筆、技術指導・セミナー講師。note, Medium, LinkedIn
-
Representative of RK Kaihatsu. Software Engineer Specializing in Development for the Apple Platform. Specializing in contract software development, technical writing, and serving as a tech workshop lecturer. note, Medium, LinkedIn

TOC