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?
As workaround: Create xconfig files per project and global ones. In the project xconfig files include the global one via
#include "path/to/File.xcconfig"
Hope this help
Related
I have several apps that using the same core framework, Since each app have some small differences i'm searching for the best way to build the framework with relevant code and parameters.
The best solution (that is not so good) that i found is:
For different code compilation - using targets that include different classes
For different configuration using different Swift flags for each configuration (e.g. debug, release...)
The problems are:
I'm using several targets and each target duplicate the project configuration and i need to maintain all. this approach can lead to some bugs if i change configuration in one target but forget to change it on other target.
For debug/release & stagingA/stagingB/production I'm creating specific configurations instead combining them (This solution is problematic since for each staging i need to duplicate it for release and debug):
release production
debug production
release stagingA
debug stagingA
release stagingB
debug stagingB
Apps struct is:
App A include CoreFramework with code adjustments for A
App B include CoreFramework with code adjustments for B
Each app have debug, release, production... configuration. This configurations affect the framework (these configuration run also on the framework)
==> I'm looking for other/better way to configure my apps project's
Don't make "code adjustments" in the framework based on which client is calling it.
Construct your framework as if it were something provided as a binary release by an external supplier. Any behaviour that may be variable can then only be controlled by run-time configuration through a public API.
I have created a custom scheme in Xcode, which loads a separate plist file and I use this build for testing the application. I've removed the Archive option from the build, so that I do not accidentally publish this development version.
My issue is that I cannot really debug this build and I am not sure why. While the default build works fine in the Xcode debugger, my scheme does not. The breakpoints work, but single stepping fails and print object commands also fail with unrecognized variable errors.
It does look like maybe some option would be needed to add debug symbols or so, but I can't find that after having tried a few things in the Xcode project options. Going back to the default Debug build is not convenient, because my new scheme has different plist options, used for testing. Btw, my new scheme was cloned from the debug one when I first made it.
Any ideas how to fix that ?
Turning off optimizations will allow you to debug the application even if in Release-Mode. Optimizations strip the executable and that is why you cannot debug the variables.
I can't add it and there's no debug optimization, yet I got the log:
ProjectName was compiled with optimization - stepping may behave oddly; variables may not be available.
More info: I went to the edit build scheme and it's set as debug mode.
It looks like you've accidentally deleted your debug configuration. To prove this to yourself, look at your project's Info pane. You should see this — but I'm betting you won't:
A configuration is just a name, so you can easily recreate the debug configuration right here in the project settings; but that won't magically restore all the corresponding default build setting values. Your best bet is to consider this project hosed: make a new project, to get the template's values for the build settings, and migrate everything from this project into it.
Somehow my Xcode project stopped giving me the option to use the Debug build configuration for the Run scheme.
I figure it has something to do with my Architectures settings in Build Settings:
Edit: It appears that the Debug configuration was deleted. I'm able to create it again by copying the Release configuration, but how do I get it to behave like the default Debug configuration?
After a lot of searching I found there is no way to get the original Debug configuration that comes with each new Xcode project. Fortunately, Debug does not differ a lot from Release. So what I did was create a new Xcode project and compare the Build Settings. On a handful of options, there was a different value for Debug and Release. I copied these values into my original project.
The most noteworthy option is "Optimization Level" option. If you want your debugger to work fully, set it to "None [-O0]" for Debug. Another one was the preprocessor macro section. Setting DEBUG=1 here will allow you to use #ifdef DEBUG and all that.
I've decided to manage different PREPROCESSOR definitions for the same application target using different schemes that are hooked up to different build configurations.
Meaning I have duplicated the Debug build configuration and gave it a new name (e.g. Staging).
Afterwords defined Preprocessor macros that are defined to each new build configuration.
Setup a new shared scheme that the "Run" step uses the new "Staging" (Debug duplicate) build configuration I have just created.
The app runs fines, but I've noticed the debugger values are all nil.
When setting the scheme to run from the "Debug" build configuration everything is fine.
The new build configuration is a complete duplicate of the Debug one with an addition Preprocessor macro defined.
This also occurs when renaming the Debug build configuration to anything else.
Is there any way to get the debugger to work with different (debug enabled) build configurations?
You probably have optimizations for that build scheme enabled. Happens to me when i run my apps in my archive scheme which always has optimizations turned on.
Check the scheme's run configuration and turn debug in there. (Run -> Info -> Build Configuration)
How are you setting preprocessor macros?
You should do it like this:
Project > Select wanted target > build settings > search for 'Preprocessor Macros' > add macro
- I'm using PRD_BUILD || BETA_BUILD || DEMO_BUILD || DEV_BUILD
Then you must check what are you actually running:
#if PRD_BUILD || BETA_BUILD || DEMO_BUILD
code
#elif STG_BUILD
code
#else
code
#endif
I experience the same issue with Xcode 6.3.1 (6D1002). I have a configuration that precisely copies the default 'debug' configuration, but from time to time debugger window shows nil values (while it is clearly visible from the program flow that values are actually present). I have no idea how to fix this since every one who is being asked about this issue hits you to turn off compiler optimizations like a Captain Obvious.