TL;DR
Is it possible to inherit custom configurations from parent project? No, not asking about inheriting some target setting.
Configurations and preprocessor macros/flags
Every project in Xcode is initialized with the two standard configurations called Debug
and Release
. It is a quite common pattern to setup a DEBUG
flag using the setting under the display name Preprocessor Macros
in Build Settings for a target (in .pbxproj
this is called GCC_PREPROCESSOR_DEFINITIONS
), which can be read from Objective-C code like this.
#ifdef DEBUG
print("DEBUG flag set")
#else
print("No debug flag!")
#endif
This also works in Swift, but then we have to use OTHER_SWIFT_FLAGS
and declare it like this:
-D DEBUG
and reading the variable value just like we did in the Objective C code above.
Many projects => xcconfig files
My app consists of an xcworkspace with a main project and several projects as dependencies. Let's call the projects my app is dependent on frameworks. Since I have several frameworks I do not want to have to setup build settings several times on.
Thus I am using xcconfig
files. I have a main config, that is the config file for the main project, let's call it Main.xcconfig
. I have another config file called Framework.xcconfig
which starts with the line #include "Main.xcconfig"
, thus inheriting settings from Main
. And of course I setup each framework to make use of said Framework.xcconfig
file.
It is very convenient to declare the DEBUG
flag when we have these config file, in Main.xcconfig
we add:
OTHER_SWIFT_FLAGS[config=Debug] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG
And thus declaring the DEBUG
flag for the configuration Debug
for the main project and the framework projects (since Framework.xcconfig
inherits from Main.xcconfig
...).
Custom configurations
What if we want to be able to profile the app but with the DEBUG
flag set? Profiling should be done with the same optimisation flags as Release
. But we absolutely do not want to set the DEBUG
flag for Release
builds.
Why not create a new configuration, let's call it Profiling
. Here comes the problem! Of course we should create this new configuration to the main project. Then we edit our scheme and for Profile and under
Build Configuration we chose the new configuration Profiling
.
Now we can set the DEBUG
flag for Profiling
in the Main.xcconfig
file.
OTHER_SWIFT_FLAGS[config=Debug] = -D DEBUG // we keep this
GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG // we keep this
OTHER_SWIFT_FLAGS[config=Profiling] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Profiling] = DEBUG
We try running the simulator and we see "No debug flag!", which is expected since for running we are using the Debug
configuration and thus not declaring the DEBUG
flag.
So we try profiling and start some Instruments measurement and open the console. There we see the message "DEBUG flag set"
It works, great!
Configurations are NOT inherited from parent project
We just checked against the DEBUG
flag in the Main project. What happens if we in some of our frameworks want to check against our flags. So we try the #ifdef DEBUG
in some framework. That works, since all frameworks have the configuration Debug
, since it is default for all project (together with Release
).
Then we try #ifdef DEBUG
in one of our framework projects and start profiling using Instruments again. And now we see the message "No debug flag!"
Oh no!
It is not working! Why not?! Well I do not know but the only reasonable conclusion must be that our projects added as dependencies - our frameworks - do not inherit the Profiling
configuration from the Main project.
For me this is unbelievable... It feels like a flaw in Xcode.
Bad solution
I don't know of any other solution than adding the same configuration Profiling
to all framework project (at least for frameworks where I know I want to check against that flag). But this feels like such an ugly solution!. I have at least 10 frameworks and it feels really ugly to have to add a certain configuration to each framework.
Alternative (terrible!) solution
Yes of course another solution would be to use the Release
configuration for profiling, and declaring the DEBUG
flag in Main.xcconfig
like this:
OTHER_SWIFT_FLAGS[config=Release] = -D DEBUG
GCC_PREPROCESSOR_DEFINITIONS[config=Release] = DEBUG
But since we want to be able to check against DEBUG
flag in framework we need to add the two lines above, declaring the flag, to Frameworks.xcconfig
as well.
And of course use the Release
configuration for profiling as Build Configuration for the scheme.
Then we can add a new configuration called AppStore
to our Main project, and only the Main project and use that for archiving the app. So far so good?
IT'S A TRAP!
No this is not a good idea! Because I just said that the configurations are not inherited between project and parent project. Thus our frameworks will not inherit this new AppStore
configuration, so when the frameworks get built/archived I have seen them "fallback" to the Release
configuration (not sure if you can chose "default"/"fallback" configuration somewhere? Maybe it will fallback to the one you used as base for the new configuration?).
But since we just added declaration of the DEBUG
flag for the configuration Release
for the Main project and all our frameworks, and it is the Release
configuration that is used for all our frameworks when we archive the app => our production app will include debug code!. This is highly unwanted and potentially dangerous.
Good solution?
I know of none... Do you? Wouldn't it be great if configurations would be inherited from parent project? That would solve everything! Apple... pretty please?
$(inherited)
. Have you tried this? – TellurionGCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG
. I am not using target settings directly. I am usingxcconfig
files and thus that would translate toGCC_PREPROCESSOR_DEFINITIONS[Profiling] = $(inherited) DEBUG
. The problems is that the configurationProfiling
is not available for our frameworks. It is a catch 22. It is the inheritance of configurations I am after! Not inheritance of a certain target build setting (likeGCC_PREPROCESSOR_DEFINITIONS
). – Vaientina<SOME_SETTING>[config=<MY_CONFIGURATION>] = <SOME_VALUE>
in.xcconfig
file OR equivalent<SOME_SETTING>= <SOME_VALUE>
for the rowMY_CONFIGURATION
inTarget Settings
is not possible ifMY_CONFIGURATION
does not exist for said target (project). It is self explanatory for theTarget Settings
case (i.e. when not using.xcconfig
files), because there is no row forMY_CONFIGURATION
! And in the.xcconfig
files the [config=<MY_CONFIGURATION>] just fails, since it does not exist. Once again, how can I make sure to inherit MY_CONFIGURATION from parent project? – Vaientina