Aaron Bratcher
Aaron Bratcher

Reputation: 6451

How can I change .plist entries based on my Scheme?

I have some App-Info.plist entries that need to change based on my environment. When I'm doing developmental work, they need to be one set of values, vs QA vs Production.

What would be nice is if I could simply have a script or something that runs based on the Scheme used to do the compilation.

Is this possible?

Upvotes: 8

Views: 5981

Answers (3)

robben
robben

Reputation: 785

You can add a User-Defined-Setting in Xcode>Build-Settings, add its values according to all the schemes listed there. And then simply use that as a variable in Info plist file. That should work just fine. This way you can avoid creating duplicate plist files, just for the sake of one or two different properties.

Upvotes: 2

Rob Napier
Rob Napier

Reputation: 299345

I think msmq's answer is valid, but you should be a little careful about using the main info.plist this way. Doing that suggests that all your versions of info.plist are almost identical, except for a couple of differences. That's a recipe for divergence, and then hard-to-debug issues when you want to add a new URI handler or background mode or any of the other things that might modify info.plist.

Instead, I recommend you take the keys that vary out of the main info.plist. Create another plist (say "Config.plist") to store them. Add a Run Script build phase to copy the correct one over. See the Build Settings Reference for a list of variables you can substitute. An example script might be:

cp ${SOURCE_ROOT}/Resources/Config-${CONFIGURATION}.plist ${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Config.plist

Then you can read the file using something like this (based on Read in the Property List):

NSString *baseURL;
NSString *path = [[NSBundle mainBundle] pathForResource:@"Config" ofType:@"plist"];
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSDictionary *dict = (NSDictionary *)[NSPropertyListSerialization
        propertyListFromData:plistXML
        mutabilityOption:NSPropertyListImmutable
        format:NULL
        errorDescription:&errorDesc];
if (dict != nil) {
    baseUrl = dict[@"baseURL"];
} else {
    NSAssert(@"Could not read plist: %@", errorDesc); // FIXME: Return error
}

There are other solutions of course. I personally generally use the preprocessor for this kind of problem. In my build configuration, I would set GCC_PREPROCESSOR_DEFINITIONS to include BaseURL=... for each build configuration and then in some header I would have:

#ifndef BaseURL
#define BaseURL @"http://default.example.com"
#endif

The plist way is probably clearer and easier if you have several things to set, especially if they're long or complicated (and definitely if they would need quoting). The preprocessor solution takes less code to process and has fewer failure modes (since the strings are embedded in the binary at compile time rather than read at runtime). But both are good solutions.

Upvotes: 5

msmq
msmq

Reputation: 1328

You can do it by performing some extra steps:

  1. Duplicate [AppName]-Info.plist file by any name. Example: [AppName]-Info-dev.plist or [AppName]-Info-staging.plist etc
  2. Map newly created .plist file in App's Target Settings. Get idea from following screenshot: enter image description here
  3. At the end, if you want to get some entry from .plist file then you need to get it like: [[[NSBundle mainBundle] infoDictionary] objectForKey:@"baseUrl"]
  4. Project setting will automatically pick correct .plist file and give you required value.

Upvotes: 7

Related Questions