Is there a way to find out the Scheme used at the run time ?
May be there is a better way to do this, I'm trying to set the Access Environment (Production vs Development) by using the Scheme name.
Currently I use #define ENV #"PROD" in App-Prefix.pch file to do this, but there is a chance to miss this when sending pkg to apple for review :(
I searched far and wide for an answer to this because I really don't like the idea of creating extra Targets nor extra Configuration sets. Both of those options just create a huge Configuration synchronization problem.
So, after hacking Xcode for a couple of hours, this is what I came up with:
Step 1: Add the key "SchemeName" to your Info.plist with type string.
Step 2: Edit your default scheme and on Build -> Pre-actions add a new Run Script with the following:
/usr/libexec/PlistBuddy -c "Set :SchemeName \"$SCHEME_NAME\"" "$PROJECT_DIR/$INFOPLIST_FILE"
Make sure and select a target from under "Provide build settings from".
Step 3: Now duplicate that scheme as many times as you like (Manage Schemes... -> Select existing scheme -> Click gear icon -> Duplicate) For instance, you can create Development, Staging, Production, App Store, etc. Don't forget to click "shared" if you want these schemes carried around in version control.
Step 4: In the code, you can retrieve the value like this:
NSString *schemeName = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"SchemeName"];
Or in Swift:
let schemeName = Bundle.main.infoDictionary?["SchemeName"] as? String ?? ""
Now, the code can configure itself correctly at runtime. No nasty preprocessor macros to deal with and no brittle configuration mess to maintain.
UPDATE: According to the comments below, $PROJECT_DIR might need to be removed depending on where your Info.plist is located in your project.
When running an app on the simulator or on your device, the DEBUG variable is set, allowing you to use it from your code:
#ifdef DEBUG
// Something
#else
// Something else
#endif
You can see this variable from your target's build settings:
As soon as the Run configuration is set on Debug (Product -> Scheme -> Edit Scheme), this variable will be set:
Related
I am currently working on an iOS project in XCode that uses configurations to specify the different API environments my app can connect to. Additionally, I use targets to override one of my project configuration's user-defined values to specify a particular configuration file to use in the app. However, this is the only value that changes in the target. I work with multiple different configuration files (maybe 10 to 20 at a time) and creating a new target for each file to update one value seems clunky.
My question: Is there a way to pass this one value in from the scheme instead of setting it in the target?
I have seen that there is a pre-build script that can be run but I have not yet had any success exporting environment variables.
CONFIG_FILE="My Config File"
export CONFIG_FILE
I have also seen that some people have had success using PlistBuddy to write the values into the info.plist file during the pre-build phase of the scheme. This may be an option as well although it would require I redo a lot of my build process. I wanted to see if there was any other options before heading down this path.
Thanks for the help.
I was able to do this by using a .xcconfig file that is updated during my pre-build action in my scheme.
I used this tutorial to learn how to set up the project: http://www.jontolof.com/cocoa/using-xcconfig-files-for-you-xcode-project/
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 :)
At first I thought a scheme was a list of values for each build type. By default, there are two schemes, Debug and Release. In the app target, you customize each scheme on the fly by choosing the values (in the default case for either Debug or Release).
However, now I think the schemes are bigger than that. Your project comes with one scheme by default, and included in that scheme is the ability to customize settings for different build types, including run, test profile, etc... So in ONE scheme you can have settings xyz for the run phase, and in ANOTHER shceme you can have settings abc for the run phase.
I'm a little confused here. Can anyone simplify this?
Thanks
From Xcode's Help:
scheme
A scheme is a collection of settings that specify the targets to build for a project, the build configuration to use, and the executable environment to use when the product is launched. When you open an existing project (or create a new one), Xcode automatically creates a scheme for each target. The default scheme is named after your project.
Here, "build configuration" is what you're calling "Debug" and "Release" above.
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.
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.