Sometime its beneficial to have different code running for each target, For example consider that are developing an app that communicates with a remote server, You may want to have two versions of your app each pointing to a different api endpoint environment. The development copy of your app will target
www.dev-example.com/api which hosts the development copy of your server app, Production copy will point to
www.prod-example.com/api which has the production copy of the server app.
In addition to above differences lets also require that the production app should have a red background while the development should have a green one.
We can achieve this conditional branching for the app at compile-time or at run-time.
By using preprocessors we can select which code is being compiled and which is not. Lets first create
AppProduction targets that represent the Development and Production API endpoints, for each target we will define a different predecessor, in oder to set the preprocessor navigate to the project file, select the target then navigate to
Build Settings search for
Preprocessor Macros and inside both
Release add a line with
ProductionEnv for the production and
DevelopmentEnv for development.
Now in your .m files you can write
NSString *api; #ifdef ProductionEnv api = @"www.dev-example.com/api" self.view.backgroundColor = [UIColor redColor]; #endif #ifdef DevelopmentEnv api = @"www.dev-example.com/api" self.view.backgroundColor = [UIColor greenColor]; #endif
Now when you run the app you will notice that the background color and the API endpoint will differ for each target.
The second way to declares custom preprocessors is by using Xcode config files
.xcconfig, create an xcconfig file for each target and assign it, to assign the config file to a target; open Xcode project file, under Configurations expand Debug or Release and set the xcconfig file.
In order to fill settings in the xcconfig we need to navigate to the target Build Settings and under Preprocessors Macros select the Debug or Release row and copy it (by using command + c), then go back to the xcconfig and paste it, you should now have a file with:
For Production target:
GCC_PREPROCESSOR_DEFINITIONS = ProductionEnv $(inherited)
And for Development target:
GCC_PREPROCESSOR_DEFINITIONS = DevelopmentEnv $(inherited)
$(inherited) tells Xcode to load the configuration settings that we filled into the target build settings in the project file.
The last way of setting the preprocessors by declaring them in the prefix header file, You first have to create different prefix header for each target, In order to set the prefix header for the target is to navigate to the build settings and fill the prefix path in the
Prefix Header row.
In the prefix header for the Production target:
While in the prefix header for the Development target:
The other way to run different code in each target(scheme) is by passing environment variables to the target. Environment variables are set in the Scheme and not in the targets.
In order to set different Environment variables you only have to duplicate the scheme and not the target.
Navigate to scheme settings, under Run select
Environment Variables and set
ProductionEnv=1 for Production and
DevelopmentEnv=1 for the Development scheme,
In code then to read it:
//isProduction will return YES for Production scheme run BOOL isProduction = [[NSProcessInfo processInfo] environment][@"ProductionEnv"]; //isDevelopment will return YES for Development scheme run BOOL isDevelopment = [[NSProcessInfo processInfo] environment][@"DevelopmentEnv"];
The second way to conditionally branch code on runtime is using plist files, Create two different
info.plist files, and set them to the targets, In the first
info.plist add a boolean with key
ProductionEnv, For the second
info.plist add an item with key
At runtime you can read the value from plist:
//isProduction will return YES for Production scheme run BOOL isProduction = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"ProductionEnv"] boolValue]; //isDevelopment will return YES for Development scheme run BOOL isDevelopment = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"DevelopmentEnv"] boolValue];