
How do you switch your app between a staging URL (for development and testing) and a production URL (for real world use)? I’ve changed my mind about my approach, because one size doesn’t fit all.
In 9 Code Smells of Preprocessor Use, I originally suggested that instead of using the preprocessor, it would be better to use a plist. Here’s the preprocessor code smell:
static NSString *const fooServiceURL = @"https://dev.foo.com/services/fooservice";
#else
static NSString *const fooServiceURL = @"https://foo.com/services/fooservice";
#endif
I wrote, “Instead of defining these URLs in your code, treat them as resource definitions and place them in a plist, organized by type.”
<key>Staging</key>
<dict>
<key>fooServiceURL</key>
<string>https://dev.foo.com/services/fooservice</string>
</dict>
<key>Production</key>
<dict>
<key>fooServiceURL</key>
<string>https://foo.com/services/fooservice</string>
</dict>
</dict>
I still think this is a good approach for a complicated app that talks to many services. Putting all the URLs in a plist makes them easy to find and manage.
But for a simple app, I’ve found this to be overkill. Instead, I create a class for the URLs, and access them through methods:
{
DebugSettings *debugSettings = [self debugSettings];
if ([debugSettings usingStaging])
return @"https://dev.foo.com/services/fooservice";
else
return @"https://foo.com/services/fooservice";
}
The plist approach also makes it harder to switch between staging and production on the fly, because all the URLs have to be reloaded. With the “define it in code” approach, all I have to do to switch URLs is change a property in DebugSettings.
What about you? How do you switch your app between a staging URL and a production URL?
Photo by Niels Linneberg (license)






I would request you don’t return NSString objects from a method named -fooURL. Instead, return an NSURL object, or name it -fooURLString
Good point, Mike! I’ve updated my fake example (and will go back and change my real code).
Hello Jon,
just stumbled across your Blog. Very nice work and thanks alot for sharing!
Determining configuration data during compile time is always a pain. It’s like turning software to hardware. Though I have to admit, that I start a new project this way when things are very simple
Regards
Nick
Hi Nick,
I’m glad you found your way here.
Engineering is often the art of balancing trade-offs. I started one project with compile-time #if’s, and that was fine as long as I was the only one who was using the staging services. But when it became important for other people to be able to switch from production to staging, I converted to a runtime switch.
One of the best parts about having a good suite of unit tests is the ability to change your mind and rewrite something!
This is an interesting read, but I have a question about security. In the scenario where the URLs are placed in a plist, wouldn’t this leave an unnecessary security risk by potentially exposing the location of the staging server, or any other item that would not be necessary in the final released version? Wouldn’t conditional build statements prevent unwanted data from being included in the final build?
For example:
URL = @"http://devserver.com";
#elif STAGING
URL = @"https://staging.com";
#else
URL = @"https://realserver.com";
#endif
Mike, that’s a good point. In my work, all staging servers are hidden behind the corporate firewall — but not everyone has that.
Also, I’ve found it helpful to allow switching between environments on the fly. Our testers don’t like the idea of qualifying one binary but releasing a different binary.
But as far as hiding unwanted data, I wonder if there’s any way to have different build configurations load different plists. Otherwise, yeah, that’s a valid use of conditional build statements.