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/
Related
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 application's Info.plist file has around 20/30 keys inside. An external SDK we're implementing requires its app key to be set in the Info.plist, but requires separate keys for debug, enterprise distribution, and release schemes.
Is there a way I can create conditional additions to the Info.plist without having to maintain three duplicates of the file (and duplicate all of the other keys, which are identical across all targets)?
Basically what I'd like is the base plist exactly as it is now, then additional new -Debug, -Distribution and Release ones, which just contain this new key. What I'm trying to avoid is repetition of all keys, since it will make adding new ones in future a hassle.
Is this possible?
There are a few different things you might try.
Xcode run script executed before app build
This is a script that is automatically executed before you build. Let's say the 3 duplicate plist files are called InfoA.plist, InfoB.plist and InfoC.plist which are all exact duplicate. When you build the project the contents of InfoA.plist are duplicated to InfoB.plist and InfoC.plist. This would require some knowledge of writing a shell script to do that, but the script is fairly simple.
You could duplicate and rename the files in the script. Or you could use command line tools to copy the contents of the main file to the duplicate files. Two command line utilities you can use to modify the contents of plist files are plutil and https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/PlistBuddy.8.html. These links might help: Add files to an Xcode project from a script?,
Modify scheme and add pre-action
Your schemes also allow you to add pre-actions to your build and you can do the same as above, but have it configured as a pre-action instead of a build phase script.
Here are a few links that might help: how to make xcode run a script before dependencies?, https://www.objc.io/issues/6-build-tools/build-process/, Xcode: Running a script before every build that modifies source code directly but you could also google for “xcode run script before build” for more links.
I am trying to create an app with multiple targets so I can use the code for different apps with small changes. I managed to work with many variables needed by setting custom keys in plist file and making user-defined-settings entries for referencing them. The problem is that with Facebook SDK I cannot do that. I have set a custom key in plist like ${FACEBOOK_APP_ID} and a user defined setting on my target like FACEBOOK_APP_ID with my app's id in it. When I compile and run, I get app_id is required error (of course if I hardcode it on plist works fine). Does anyone know of a solution not requiring to use different plist files for each target?
After struggling for days with that without a good solution, I found that if I set preprocess info.plist to Yes, I can add user-defined settings to build-settings as usual and they are available at runtime. Now, I only have to enter the three variables to the target configuration and there is no need for a preprocess file or preprocess macros.
Update:
I have added FACEBOOK_DISPLAY_NAME, FACEBOOK_APP_ID and FACEBOOK_URL_SCHEMES to user defined settings (from Editor->Add Build Setting menu in Xcode) with the proper details for each target. Then In Info.plist (I use one globally for all targets) I added the options Facebook SDK guide states but instead of static values I added ${FACEBOOK_APP_ID}, ${FACEBOOK_URL_SCHEMES}, and ${FACEBOOK_DISPLAY_NAME}. Then on build settings I chose YES to preprocess plist file. Now I can change the info in my user-defined settings for each target and works ok (without the need of an extra preprocess file and the need for defining macros)
You can try use the macro in your difference target, in the code you just define the value by the macro. exampe:
#if project1
//the app id const
#endif
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.
I've got a relatively large Xcode project that produces a single app. However, I have many clients/customers who require deep customization and branding of said app. These configurations include different graphics, a few different interfaces and implementations, and, perhaps most importantly, .xcconfig files.
My Xcode project has a dedicated group that points to a particular client's customization folder on disk, so by opening the Xcode project and building, you get a build of the single app with the current client's customizations. To switch to another client, I change where that group points to on disk. (I also change and switch-back the xcconfig "Based On" settings in the project's Info pane to reload the full xcconfig inheritance; Simply changing the group containing one or more xcconfig files doesn't reload this!) This has worked great for 100+ clients. It's a little tedious to switch this folder every time you need to build the app for a different client and ensure the xcconfig is correct, but it works.
Now I'm in the process of automating builds via the command line, and running into troubles. The quick and dirty solution to pointing the aforementioned Xcode group at a different customization folder was to copy the ProjectName.xcodeproj/project.pbxproj file to ProjectName.xcodeproj/project-template.pbxproj and put placeholders inside this file that can be grepped and replaced with the name and path of the desired customization folder. Then, temporarily overwrite project.pbxproj with the modified project-template.pbxproj, and build to get the correct app.
As you've probably observed, the project.pbxproj was duplicated and modified, and will therefore get out of sync as developers modify the original and forget to also update the template. And besides, I shouldn't really be messing with pbxproj files in this fashion anyway -- that's Xcode's private stuff.
So, is there a better way to tell Xcode about a folder full of resources, code, and config files perhaps during the Build Phase with a script or environment variable, rather than at the project group level? The most complicated bit seems to be the xcconfig chain, since each client has their own xcconfig file that inherits from the single app's Debug, Development, and Distribution xcconfig files.
Sorry for the long-windedness of this question, but it's a little complicated! Any suggestions would be greatly appreciated!
I think you would way better off using the targets feature in Xcode. Have one project and the resources of every clients in that project.
You can then duplicate the target you already have (right-click on your target, by selecting the project file in Xcode's Project Navigator).
All your targets will be compiled with the same code. You just need to change the resources in Build Phases > Copy Bundle Resources to have different app created for each target. No need to look at Xcode's internal files.
You can even change the code in your source files by adding a preprocessor macro in your build options (something like FIRST_CLIENT=1) and then look for these definition in your file with #if FIRST_CLIENT.
I have a project set-up like this and it works pretty well :