Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

xcode

ios

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?

like image 377
Aaron Bratcher Avatar asked Dec 18 '14 14:12

Aaron Bratcher


People also ask

How do I move plist to another folder?

All you have to do is click on the blue project icon -on top of the left- and go to the “Build Settings” tab, search “Info. plist” and choose “Info. plist File” section. Then change the information of the section from your folder's name.

What is difference between target and scheme in Xcode?

A Target specifies a product to build and contains the instructions for building the product from a set of files in a project or workspace. An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute.


2 Answers

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.
like image 178
msmq Avatar answered Sep 22 '22 12:09

msmq


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.

like image 33
Rob Napier Avatar answered Sep 21 '22 12:09

Rob Napier