How to configure independent sets of runtime settings in Xcode - iphone

My iPhone application connects to three different servers, say: production, staging and testing. There is a bunch of configuration values that the application uses depending on to which server it connects to, e.g. Facebook App ID, TestFlight team key, etc.
I'd like to have all the settings in GIT and only select which configuration the application supposed to use when compiling or releasing. For example, when testing is selected, Product -> Run in Xcode runs the debug version of the app connecting to testing, and Product -> Archive creates the IPA file with the release version that also connects to testing.
I don't want to create more build configurations than debug and release (because that would mean 6 different combinations of build configurations/run-time configurations). The ideal solution, as I see it, would be that I have three schemes: production, testing and staging, and each scheme selects one of three Info.plist files to use with the application. That would allow me to not only define different run-time settings, but also different application versions or bundle identifiers depending on the back-end server. But it doesn't look like I can configure the Archive action in any other way apart from selecting a different build configuration. Any ideas if that could be achieved in any way?
Edit: To make it a bit more clear, production/staging/testing is the back-end server, not the version of the iOS application. The iOS app comes in two versions: debug/release. In other words I may want to run a debug version of the application connecting to the production server for example to debug a crash caused by JSON returned from that server. I could have named the servers as A, B and C for the sake of clarity.

A good way to do this would be with build configurations and C macros. This avoids having to create a separate target for every configuration which is not really the correct use of targets.
First you want to set up the configurations at the project level:
You can create different configurations for debugging, enterprise distribution, and any other type of special build you want.
Next you can define some macro flags for each configuration which will be passed to the compiler. You can then check for these flags at compile time. Find the "Preprocessor flags" build setting at the target level:
If you expand the triangle you can define different values for each of your configurations. You can define KEY=VALUE or just KEY macros here.
In your code, you can check for the existance of these macros, or their value (if there is one). For example:
#ifdef DISABLE_FEATURE_X
featureXButton.hidden = YES;
#endif
// ...
#if FOOBAR_VISIBLE == 0
foobarView.hidden = YES;
#elif FOOBAR_VISIBLE == 1
foorbarView.hidden = NO;
#else
#error Invalid value for FOOBAR_VISIBLE
#endif
You can pass in string values as well, which must be wrapped with single quotes in the build setting, e.g. DEFAULT_LOCALIZATION_NAME='#"en"'.
You can also configure which configuration is used during Debug and Archive time using the Schemes editor. If you choose "Run" or "Archive" in the Schemes editor you can select the appropriate configuration.
If you need to parameterize entries in the Info.plist file, you can define their value using a custom build setting. Add a custom build setting for your target:
And then give it an appropriate value for your different configurations:
Then in the Info.plist file you can reference this setting:
Note that the one limitation of this approach is that you cannot change the following items:
Settings.bundle
Additionally, in older versions of Xcode without asset catalog support, you cannot change the following items:
Icon.png
Default.png
These cannot be explicitly defined in the Info.plist file or anywhere else, which means you need different targets to change them.

I would suggest using different build targets for each environment. I successfully used this model before. In your project's settings you can duplicate the current target and change the build settings as needed. There's an Info.plist File property that will let you change the default plist for that target.
After that, you can create a scheme for each environment that will use the according target.
You can get a step further and use different bundle id for each target and different names. That will allow you to install both the staging and the production builds on the same device for example.
The only downside in this is that you have more work when you want to update provisioning profiles.

Here's a much easier solution if the concerned libs allow to set the keys in code, meaning that you can have production value in your plist file, but change them in your AppDelegate (or whichever file they are first used in).
Works with facebook, twitter and google sdk at the moment.
Ex:
#ifdef DEBUG
// Facebook
[FBSettings setDefaultAppID:#"SandboxID"];
// Fabric / TwitterKit - must be called above [Fabric with:#[TwitterKit]];
[[Twitter sharedInstance] startWithConsumerKey:#"SandboxKey" consumerSecret:#"SandboxIDSecret"];
#endif
Same in Swift, just use #if instead of #ifdef.
Note about Facebook This worked with version 3 of their SDK, I'm not sure it's possible with later versions.

It is probably very low tech but I just have a method called apiURL() that returns the URL of the API I want. I have localhost, stage, and production and I simply uncomment the one I want. It's worked well for me so far. I've only forgotten to switch it back a few times. Oops.

Related

Remove static library based on certain preprocessor macro

I have one functionality and it is depending on one static library. Actually, Apple is not allowing this static library because of few reasons. So, for now we have decided to add one Preprocessor macro and based on that functionality should be switch on/off.
I have switched my code to behave based on that Preprocessor macro. But I didn't find any to link or unlink the library based on preprocessor macro. I can not allow to link the library all times, as I have already told apple is not allowing it.
One solution that I already know : I can create the multiple target. But problem is that my app has already lots of targets. So, again to manage this I have to create one more target for each target those are already created.
Any help will be appreciated.
Edit:
I have created configuration like Francesco suggested. But One quick question I have let's say If I will remove the path of library from "Library Search Path" then that library will not be linked to app? Because that library is still in target of App.
Instead of targets you can create multiple configurations. And you can change the linker flags there.
I did this think for Sparkle framework (which is not allowed on App Store)
EDIT:
To create/manage the configurations click on the project in the sidebar. Then in the main window select again the project, not the targets, and select the "Info" tab.
Together with Deployment Target selection and Languages you will find a Configuration section.
You can add or remove configurations from there.
Then to launch it you have to go to Product -> Scheme -> Edit Scheme -> Build Configurations.
You can duplicate an existing scheme and choose the correct configuration from there.
You can remove the static library from the list of linked items by setting a (somewhat undocumented) Xcode build setting:
EXCLUDED_SOURCE_FILE_NAMES = libUnwanted.a
You can do this in an .xcconfig file or in the normal target build settings UI (by adding a custom build setting). If you for example set this in the "Release" configuration Xcode will include the library in the "Debug" build but omit if for the release build.
EXCLUDED_SOURCE_FILE_NAMES works with all kinds of files that can be added to a target: source files, resources, libraries, ...
Here's how to do this step by step:
Open the target build settings by clicking on the blue project icon in the Project Navigator and select the Build Settings tab.
Click the plus icon to add a "User-Defined Setting"
Name the setting "EXCLUDED_SOURCE_FILE_NAMES"
Expand the Configurations for the setting and set the "Release" value to the name of your unwanted library.
Credit of this answer goes to Nikolai Ruhe & Francesco
I have combined the answer of both and I got the solution.
Here are the steps that I followed.
Created new configuration. ( To know how to create configuration see
the answer of Francesco)
Added flag EXCLUDED_SOURCE_FILE_NAMES in user-defined setting of Build settings. (For steps see the answer of Nikolai Ruhe). And in this flag I have added the name of my static library under the my custom configuration.
Here is a good tutorial that will definitely help you : Remove tesflight from Distriubtion

Is there a way to know if the ipa was archived vs just run directly in xCode?

I like to create slightly different code when running the app from within xCode vs when archiving in xCode.
is that possible somehow with a compiler setting that one cann access?
Just like BuildConfig.DEBUG on Android?
The recommended way to do this in Xcode is to use Schemes. They are already setup and you will see them under Edit Schemes. When you are running normally in Xcode, you are typically running the Debug Scheme, there is also a Scheme set up for "Archive" (basically a release build). Xcode supports setting up as many schemes as you like and to set one up takes only minutes.
In the example illustrated in the picture I've set a pre-processor flag "DEBUG" to be set to 1 when running the "Debug" Scheme, or debug mode in Xcode. It is NOT set in other schemes. This makes it possible to have code that is only included in Debug builds, and not included in Archive builds.
That's pretty much all it takes, then you simply do something like this in your code:
#ifdef DEBUG
<Some code that is included and executed when running the Debug Scheme>
#else
<Some other code that executes at other times in other Schemes>
#endif

XCode4: Project settings differs from Targets settings?

I don't know the reason but this happens at times. I am setting bundle id correctly and also installing mobile provisioning profile correctly but sometimes build settings for Project doesn't detect my certificate automatically(when I select iPhone developer in signing) but it does for build settings of Targets! I don't understand how this could be possible? AFAIK automatically detection of certificate related to bundle id in plist file that correspond to set in certificate which is I am making sure they both matches! Could anyone think of any scenario where Project and Target differs in signing?
Thanks.
Regarding Project and Target settings; I believe the reasoning here is that one Project can contain multiple Targets if needed. Its usually one in the general case, the Target for your main application.
Therefore, the Project Settings will be the big, overall settings which apply to ALL your targets and I believe you will be able to customize them for different targets if needed.
The general guideline I follow is, I always set the certificates and profiles etc in the Project Settings, then go to the Target settings to verify that they have been applied (sometimes they are not automatically applied); and if not, reapply them there. This always works out for me without a prob.

Distinguishing development iPhone app versions

I like to keep a store-bought version of my iPhone apps on my phone so that I can reproduce any customer issues that come up, but I obviously also want to run the most current development version. I can install both (one from iTunes, one from xCode) but I'm interested in ways that I'm better able to tell the two apart. I could just change the name or icon temporarily, but that doesn't seem very failsafe, i.e. I might forget and ship it with the wrong icon.
Is there a happy funtime developer way to do this?
I was inspired by Eric's idea of adding a user-defined setting to the project but I didn't want to run a script every time I built the project.
We know that the iPhone looks for icon files named "Icon.png" by default. It turns out that the "Icon File" setting in the project plist isn't necessary at all if you've named your icon properly. However, if there is no file named "Icon.png" in the project, xCode looks at the value of the "Icon File" setting.
I set a user-defined setting in "Debug" called "Icon_Name" to a non-standard icon name, "DevIcon.png" and "ReleaseIcon.png" for the "Release" config. The "Icon File" setting in the project plist can now be set to ${ICON_NAME} and will take on the value of whatever config file we're using. Now building under two different configurations does use two different icons.
Edit: For multiple icons (high res, small, ipad, etc) I made a slightly different approach. The user-defined setting is now "IconPrepend" which is "Dev" for debug and "Release" for release config. I'm now using "Icon Files" (rather than "Icon File") in the info plist which takes an array of strings. Each string is prepended with ${ICONPREPEND} so that debug configurations look for "DevIcon.png" or "DevIcon#2x.png" and release configurations look for "ReleaseIcon.png" or "ReleaseIcon#2x.png".
Here's an idea - if you set a User-Defined Setting in your Project Build properties for Debug only along the lines of USE_DEV_ICON=YES (or something). Then, using the "Run Script" option in your Build Target you could copy different icons based on which Active Configuration you called.
Something along the lines of (pseudo-code):
if ($USE_DEV_ICON == YES)
cp DevIcon.png Icon.png
else
cp RealIcon.png Icon.png
Then every time you build, depending on the active configuration, it will copy the correct icon.
Most probably there is no other way. Your app store version and development version is completely different to the OS. If you want to distinguish, you need to change the icon or name. You can also include some debugging label (e.g. version number) in your development version, but you may also forget to remove this.
Maybe you'd just buy a second iPhone? Seriously. On your place I'd ask the same question as you do, but I will definitely take such a solution in mind. If I was an iPhone (or whatever other specific platform) developer, I'd most probably have more than one.
Couldn't you make a new target which has a different Bundle name in the Info.plist file, and use this target whenever you want to build the app to run on your iPhone?

iPhone - Switching between local and production environment settings

I am developing an iPhone app which uses a server somewhere to fetch its data. Somewhere in the app's source code I hardcoded the URL to use to connect to. This is fine, except that I don't always want to test using a production server! I don't want to mess with live data, just to test something locally. So I set up a local version of that same server. But in order to make the iPhone app use that server is to change the hardcoded URL in the source code.
This is a little bit of a pain in the ass to do if you're often switching between the two servers. Also, I might accidentally release the app which still uses the local URL!
I was thinking that maybe XCode can help me with this since it has the notion of a "Debug" and a "Release" configuration option to build with. So my question is: can I somehow change the Debug configuration in a way that it points to local server URL? Maybe through pointing to a properties or plist file which contains the environment specific URL. I could then make two versions of this properties file and make the debug configuration point to one, while make the release configuration point to the other.
Does anyone know how I can accomplish this?
Put this code where you need to use the configuration based on the mode (debug/release) = (development/production).
The best place to put it is on the "ProjectName"_Prefix.pch file.
#ifndef __OPTIMIZE__ // __OPTIMIZE__ is not enabled, it means that the active config is Debug, so here you have to put your code for development mode
// For example
#define SERVER_URL #"http://my.test.server/something"
#else //__OPTIMIZE__ is defined, so put here your production code
// For example
#define SERVER_URL #"http://my.production.server/something"
#endif // __OPTIMIZE__
Cheers,
VFN
In one of your header files (such as the pre-compiled header file) define macros with the URL. Take a look at this article and use a similar approach.
Incidentally, I'm using the logging approach from this article in all my apps - it works like a charm, I strongly recommend it!
You can define pre-proccessor macros in xcode by simply editing the gcc language settings:
Go to the Project menu and select "Edit Project Settings". Go to the "Build" tab .
Go to the section labeled "GCC 4.0 - Language". There is a setting named "Other C Flags". Add all the "-Dwhatever" macros you want there.