Switching between Staging URL and Production URL

November 29, 2011 — 9 Comments

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:

#if STAGING
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.”

<dict>
    <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:

- (NSString *)fooURLString
{
    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.

Question: What are your approaches switching your app between a staging URL and a production URL? Leave a comment below.

Jon Reid

Posts Twitter Facebook Google+

I'm passionate about not just improving our code, but improving the way we code.

9 responses to Switching between Staging URL and Production URL

  1. I would request you don’t return NSString objects from a method named -fooURL. Instead, return an NSURL object, or name it -fooURLString

  2. 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!

  3. 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:

    #ifdef DEBUG
    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.

  4. Jon,

    Thanks for the information. I have been working in Android for the past year and had a solution that worked really well for us. Now I am finding my way back to iOS/Mac again and I am really struggling with the best method for switching between environments.

    How do you set your “DebugSettings”? Hidden screen, secret back door, something else?

    Here are some of the ways I have been considering from what I consider best to worst.

    Option1
    ———-

    My current thought (not implemented yet) is to code the production environment into the code. Then look for a specially named file in the apps documents directory. If that file exists I load it and use it’s configuration instead.

    I will provide a command line tool to QA and dev that wraps this functionality and can switch to a given environment quickly. I will use one of the MobileDevice.framework wrappers to accomplish the file copy.

    Downside is the app will need to be restarted when I want to load the new configuration. Configuration is lost when app is uninstalled and reinstalled.

    Option2
    ———–
    Create another app that the production client looks for and calls into to get it’s configuration.

    Option3
    ———–
    Have the server move folks between environments.

    Have any recommendations?

  5. As an update I found the solution that solves the problem for me. I am using https://github.com/xslim/mobileDeviceManager and a script that is checked in. The developer can create their custom configuration and copy it to the documents directory. Now we keep production checked in and have a runtime check for our custom configuration file.

    Here is an example of the tools usage:

    $ mobileDeviceManager -o copy -app “com.domain.MyApp” -from ~/.myAppConfig/app_override.plist

    This way the developer can keep their custom configuration in their home directory (out of source control) without fear of accidental checkin. We already use process like this for other desktop and android apps so this fits our process really well. This has the added benefit that if a testers device is failing we can point it at a custom debug server with extra logging to simplify the debug process and not need to deploy a new binary to that device during internal testing.

    I hope this can help someone else.

Leave a Reply

*

Text formatting is available via select HTML.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> 

Have you Subscribed yet? Don't miss a post!