Environment variables in different schemes for use in code Xcode Swift - ios

My project, with one target, has three configurations:
Debug, UAT, Release
I have four shared schemes that use these configurations for build/run/test/profile/analyse/archive.
In my app I have different bundle IDs for each scheme. This was easy to change, I did so within Product Bundle Identifier for each configuration.
However, I want each scheme to connect to a different REST API. (dev.example.com/api etc)
What is the best way of setting a variable for each environment and then using it within my app?
I have looked at:
1) Swift Compiler>Custom Flags (Mine are currently $(inherited) "-D" "COCOAPODS")
2) NSProcessInfo.processInfo().environment
3) Adding a Configuration.plist file for each environment
Basically, it's not clear to me which is the best way of doing this.
Thank you for your help.

I usually prefer this :
I define constants in different .xcconfig files, then I use them for keys in one single plist file.
Let's say you'd have this in your debug.xcconfig :
<pre>
BASEURL = api.dev.com/api/"
</pre>
Then, in your plist, you'd add a key baseUrl = http://${BASEURL}
Then, in code, you'd access it with
NSBundle.mainBundle().infoDictionary?["baseUrl"]
This is a great article if you want to know more about xcconfig : http://www.jontolof.com/cocoa/using-xcconfig-files-for-you-xcode-project/

Personally I use different .plist files for each scheme. I tried the custom flags approach at first, but as the app grew i needed more and more scheme-based configurations and things got messy.
Different .plist files worked wonders for my project at least.

Related

iOS How to create config file per target

Hi I have been working on my first iOS app. I have completed all of its requirements. Now its time to distribute. But before distribution I want to manage things well.
I searched for it and got "Target" as what I wanted. now just suppose the following requirement:
I have Client 1 and Client 2, and they both have different web addresses and Images and App Master password.
Now the 1 thing is to change them manually before making the build. But I wanted more proper way. So I thought of creating a Config file per target per client. but I am not getting it done.
Because after adding "Config" file for client 1 I am unable to create the 2nd "Config" file for Target 2 which indicates the client 2.
Please help me I am confused. Thanks in advance
Once you have different targets, you also have different property list attached to it. This allows you to customise some elements of your configuration for a specific target.
In this example, I duplicated my target and have now two targets, two schemes and two .plist files. I can customise one .plist without affecting the other target.
Another way to solve this problem is to create two configuration files in Swift. For instance, you can create a AppConfig struct foreach app configuration and attach it to one target at a time. It could be under AppConfig.swift and AppConfigDemo.swift, both with following code but different url in it.
struct AppConfig {
let urlString = "https://myclienturl.com"
}
The key is to include each file into the right target. Here, this AppConfig.swift is available with Test target. It won't be accessible in TestDemo target. You can do the same foreach one of your client.
Make sure they have different names, Info.plist and Client2-Info.plist (for example).
and you can set targets config file in its build settings. just search for Info.plist and set the relative path to the configuration you need.

How do I distribute my default settings plist file with a single known name, and have a different file depending on the selected target?

I want my iOS app to read default settings from a configuration file. It will be a plist file. The source code should have the path and name of the file hard-coded and my code wold just open the file and read the contents at startup. But I would need to have two files with the same name and path in the Xcode project to do it this way. Of course, each file would be for a different target and only one would get included in the distribution package.
I have not investigated renaming files in a build script. I hope there is a better way.
I have considered having different file names and then searching for the file within the known relative path. This seems easy and is the option I will go with if there is no simpler or more clever way to do this.
So is there a way to pick from two different files, based on the selected target of the app, and have that file end up in the distribution package with a specific name (always the same name)?
There are different approaches that can achieve it. One is to create >different targets, each one of which employs different Info.plist. Each >time a target is selected, a different Info.plist will be used, hence we >will be able to differentiate variables like token, URLs for different >builds. The alternative approach is to put your build configuration >settings into .xcconfig files and refer those in your project info.
It could also be achieved by using Bundle Identifiers
I hope this link will be helpful to you,
https://www.appcoda.com/xcconfig-guide/
https://www.appcoda.com/using-xcode-targets

Set build variable in scheme instead of target

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/

Facebook SDK iOS on multiple targets

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

iOS Settings Bundle with items that appear only in Debug builds

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.

Resources