I currently have a number of special "flavors" of an iPhone application that I need to build. Ideally, I would like to have a scheme for each "flavor" and each scheme would define (or set) one or more preprocessor definitions that I can use to branch on in my code and possibly even preprocess my info.plist file. This can obviously be done with multiple targets, but since I could have many different "flavors" of the app, it would be great to do it with schemes to keep the target count down. My current thought is to add these preprocessor definitions during a pre-action script, but I can not for the life of me find any way to update GCC_PREPROCESSOR_DEFINITIONS. Since it is an environment variable, shouldn't I have access to append onto GCC_PREPROCESSOR_DEFINITIONS?
Worse case scenario you can do a pre-build script for the scheme. You'll have to include the script for every scheme though:
I would prefer to attach it to a Configuration:
Then you can easily add preprocessor macros for the various configurations, like I have here for Debug:
The <ProjectName>_Prefix.pch file is a great place to put macros that effect the whole program, like I have here:
In my example we're effectively turning off console output when not in debug mode, providing a little speed boost.
To meet my requirement of allowing schemes to set preprocessor definitions, the best solution I have come up with is to have scheme pre-action and post-action scripts modify a xcconfig file. This file, in turn, updates the build configuration, setting the preprocessor definitions and will even allow me to define preprocessor definitions to conditionally modify the info.plist. If anyone else goes down this route, make sure you take into account how this file is handled by source control.
This article's question and associated answers was helpful to me: How to append values in xcconfig variables?
How about defining multiple targets and defining pre-processor macros in the target-specific build options? Then you need only have one scheme, and you can build all the targets in one shot, all with their own specific build configurations.
If I understood your question correctly, you are looking to add some of user defined preprocessor macros to your source code, there is a way to add them in your target using Xcode. (e.g. GCC_PREPROCESSOR_DEFINITIONS = USE_TAPJOY )
Step 1) Decide marco name e.g USE_TAPJOY
Step 2) Go to target-> select tab "Build Setting" (Make sure all tab is enabled)
Step 3) in search box search for "Preprocessor Macro")
Step 4) Check for Debug/Release section
Step 5) Enter your Marco there
Step 6) Use this macro in your source code as below
For conditional include
#ifdef USE_TAPJOY
#import <Tapjoy/Tapjoy.h>
#endif
For conditional source code
#ifdef USE_TAPJOY // Tapjoy Connect Notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(tjcConnectSuccess:)
name:TJC_CONNECT_SUCCESS
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(tjcConnectFail:)
name:TJC_CONNECT_FAILED
object:nil];
#endif
Good luck
Related
I have a project in Xcode (swift) and I want to build it in two ways. a build with a framework (embedded in project) and another build without that framework.
Is there any way to do this with minimum changes for each update?
I means something like if #available statement which determine whether a particular framework is embedded in project or not.
something like:
if ... {
import framework
}
Finally, I found my solution.
In this case we should do four main tasks:
creating a scheme for separate builds
defining an Active Compilation Condition
excluding the frameworks we want to remove from build.
putting depended codes in #if scope.
First of all, we need to create two build configurations by duplicating and renaming debug and release build configurations (In this case, I named those as Debug_no_charkhune and Release_no_charkhune). For first step, with choosing main scheme and clicking on duplicate scheme in edit scheme page our new scheme is ready. just remember to change its name and build configurations in all tabs with new build configurations. For second step, we should go to Build settings tab in project setting page and set Active Compilation Condition values for wanted schemes. (In this case I defined CHARKHUNE as a condition).
Now we need to exclude unwanted frameworks in new scheme. for this, we should add framework name as a string in Excluded source file names section in project's builds settings like image:
Now we're ready for it. we should put codes that are related to excluded framework in an if statement like this:
#if CHARKHUNE
import charkhunePayment
#endif
At last. thanks #Cristik and #dfd for their advices.
Hope you enjoy ;)
Till now, I know there a couple of solutions for multiple environment settings.
Solution 1: Prepare different plist files for the different environment. Then, use Add Run Script Build Phase to copy the specific plist file to overwrites project plist file.
Solution 2: In PROJECT -> Info -> Configurations, duplicate a existing configuration. setup scheme -> Info -> Build Configuration. Then, in the targets -> Build settings, add user-defined settings.
$(kBaseURL)
Solution 3: Add preprocessor macros in PROJECT -> Build Settings to define the environment. Then, in source code write the macro to switch environment setting value.
#ifdef DEBUG_ENV
#define kBaseURL #"http://debug-server.a.com/api/"
#else
#define kBaseURL #"http://production-server.a.com/api/"
#endif
There are several other ways to switch environments settings.
Which way is the best practice to do this?
I think this is also a question of taste and of what you are actually trying to achieve.
For example you might want to have a debug configuration that does not talk to the internet and rather uses HTTP stubs to mock the requests. This won't be possible by simply copying plists.
Personally I prefer creating build configurations for each environment, then set a compiler flag for each config that switches precompiler macros. I also like to take some more tricky steps to modify the bundle id and app name for all configs and then create a build scheme for every config. This results in me being able to build all environments from Xcode and install all apps on the same device, as the bundle ids are different. It's a bit tricky to set up though. However once it runs: perfect for my use cases :)
My project uses a Settings.h to store common configuration settings like the server url etc. I have been tasked to create a new app from this code where only few parameters such as the server url , icon etc are different.
I have a new target in my xcode workspace for this. I have 2 Settings.h in separate folders like
awesomeProject/Settings/Settings.h
oldProject/Settings/Settings.h
The Settings.h is included in a few places in the project (not loaded via .pch). I have tried setting header search path for both the targets and this didn't work (as in the compiling awesomeProject target included the oldProject's Settings.h).
Is there a way to #include Settings.h based on the target without resorting to sprinkling #ifdef .. #endif constructs ?
If I understand right and you have separate targets to that need to include different header files so you should be able to set the 'Header Search Paths' differently and get the correct file. You said you tried this but you might want to try doing a clean and full rebuild.
I would try to leverage preprocessor to accomplish your goal. One option is to define in build settings for each target macro SETTINGS_H, which will be "awesomeProject/Settings/Settings.h" or "oldProject/Settings/Settings.h" depending on the target. Then in source code you can #include SETTINGS_H. Of course, you might need to tweak header search path to be able to find each header.
Honestly speaking, #include SETTINGS_H is a tad ugly for my taste, so I prefer another approach. You can keep single "Settings.h" file, but use macros for constant values. For example, in "Settings.m"
NSString *const kServerURL = SERVER_URL;
and in target build settings add to GCC_PREPROCESSOR_DEFINITIONS
SERVER_URL='#"http://stackoverflow.com"'
You might need to play with quotation marks to achieve the desired effect. To organize project better, you can move macros' definitions to .xcconfig file and keep separate .xcconfig files for each target.
I have user setting that I want to have available in settings, but only if it is a debug build, or perhaps it is one of two separate targets.
In the settings bundle, I have the item in Root.plist, but I want it to only be visible if it is a debug build.
Is there any way to make something conditional in the settings bundle?
I know that I can use separate settings bundles for two different build targets, but that would require me to maintain two separate settings bundles, and that seems pretty messy.
Any ideas?
You could have a Settings.plist with everything in it, then add a build step for the target where you don't want to expose one of the settings. In that build step, use PlistBuddy to remove the setting you don't want.
This has the advantage of letting you use the great tools Xcode provides for editing your plist, while remaining DRY.
As far as I'm aware, there is no way to put conditionals (or, for that matter, any form of code) in the settings bundle. You could create an in-app settings page that is only displayed in debug mode or something of the sort though.
Edit your schema to run a script as one of the build pre-actions. The script can generate Root.plist and and include the extra settings or not as appropriate. Although you could use whatever language you want for your generator script (Ruby, Tcl, Forth,...), I would just use the C Pre-processor.
Specifically,
Create a file, Root.plist.orig which includes all the regular settings. Include your extra settings wrapped in #ifdef DEBUG and #endif
Create a build run-script pre-action in your schema. The script should consist of a shell command to run the pre-processor. So something like (I don't have the details 100% correct): cpp -DDEBUG Root.plist.orig > Root.plist
In you application delegate, read the plist and put into a dictionary. Then use a compile time flag for the DEBUG build, which turns on some code that adds or modifies the plist turned dictionary. From then on don't access the plist directly, interface to the dictionary through a method in the app delegate.
EDIT: write some command line level software to take your standard plist, and based on the build configuration, change it, then output it back as a plist that then is copied into your app bundle, as a build phase. You may be able to do this with standard system cli functions, or if you do it in a custom mac cli program, its a simple thing to construct.
Is there any way to create a project/application which will have multiple targets.Its same as how we create multiple targets for an iPhone application in XCode.Basically I have an app which has to be made for different targets, with almost all the similar functionalities but with little change.
You can use Configuration Manager to add additional configurations to the list of Debug and Release. Then for each configuration go to Project/Properties/Build/Conditional compilation symbols and add a symbol used with your configuration or target, eg. make it SILVERLIGHT;WINDOWS_PHONE;CUSTOMVERSION1
Then in your code you can say
#if CUSTOMVERSION1
Debug.WriteLine("This is a CUSTOMVERSION1");
#else
Debug.WriteLine("This is not CUSTOMVERSION1");
#endif
Otherwise - if you want to make bigger changes - you would create another project and link files from one project to another project - project/Add/Existing Item//Add As Link(an option in the "Add" button menu). You can then add more files or add different versions of these files as needed. You could use Project Linker to do it faster.