Define ENUM values in a .plist in Xcode - ios

I use a configuration.plist file to configure certain parameters in my application and initialise few classes based on the contents of this plist file.
However I want to expose to the developer a list of options that can be selected as below(per say),
I can this kind of option available in application info.plist file but I don't get to see anywhere else on how I can achieve this.
I'm looking at getting a drop down list showing the list of available options, Possibly an ENUM list.
Appreciate any assistance.

You can read from the .plist file:
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString* region = [infoDictionary objectForKey:(__bridge id) kCFBundleDevelopmentRegionKey];

Related

Can i define User Defined key at info.plist

Can i defined a user defined key with given name "SomeKey" and can i access with [[NSBundle mainBundle] objectForInfoDictionaryKey:#"SomeKey"]
Of course!
Apple Docs:
Custom Keys
iOS and macOS ignore custom keys you include in an Info.plist file. If
you want to include app-specific configuration information in your
Info.plist file, you can do so freely as long as your key names do not
conflict with the ones Apple uses. When defining custom key names,
prefix them with a unique prefix, such as your app’s bundle ID or your
company’s domain name, to prevent conflicts.
And you can get the value by doing something like this:
Objective-C
[[NSBundle mainBundle].infoDictionary objectForKey:#"SomeKey"];
or
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"SomeKey"];
Swift
Bundle.main.infoDictionary?["SomeKey"]
Reference:
Info Plist Docs
Reserved Keys
We do access the application bundle info.plist like shown below,
Objective-C
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *appDisplayName = [infoDictionary objectForKey:#"CFBundleDisplayName"];
Swift
let appDisplayName = Bundle.main.infoDictionary?["CFBundleDisplayName"]
Similarly, If you have added any custom key-value pair in the info.plist you can surely access it. Just replace the CFBundleDisplayName with your key.
Refere this
Short Answer: Yes.
Read: Custom Keys in Apple Docs

Objective-C Access Specific Item in Plist

I have created an app, and I am trying to access a URL that is stored in a plist file. At this state I am just trying to log the contents out. I am aware similar questions have been asked before, but I am asking specifically to my scenario how to I access Item 0. I am trying to access Item 0 inside InternalViaSafari this manifests itself inside the URLValidator and then that inside the Root. The code I have so far is:
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"plist-file-name" ofType:#"plist"];
NSDictionary* plist = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSString* name = [plist valueForKeyPath:#"URLValidator.InternalViaSafari"];
NSLog(name);
You cannot use keyPath like that for this as far as I know. InternalViaSafari is not a property of URLValidator dictionary. What's more InternalViaSafari is an Array not a String in your plist.
In order to get this string you'd need something like this :
NSArray *internalViaSafari = plist[#"URLValidator"][#"InternalViaSafari"];
NSString *name = internalViaSafari.firstObject;
What happens here, is that you get the value under the URLValidator key from your plist dictionary. This value is also a Dictionary (this can be clearly seen in the plist screenshot you shared), so you get the value under the InternalViaSafari key. This value is in turn an Array, which has Strings as its elements. In this example I extracted the first entry from this array.

how to get INFOPLIST_FILE name iOS

Each target in my project has to has different info.plist. I want to get the name programmatically from BuildSettings> Packaging> Info.plist file but somehow I cant really retrieve the plist list filename. Is there a way i can retrieve the plist programmatically?
NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleInfoPlistURL")
only gives me
Info.plist -- file:///Users/XXXX/Library/Developer/CoreSimulator/Devices/XXXXXX-9B37-4C3D-88E9-XXXXX62B470/data/Containers/Bundle/Application/7XXXXXXXB-BE36-4D3E-9C90-XXXXX/PROJECT%20KOL.app/
Actually NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleInfoPlistURL") returns an NSURL object. Extract the absoluteString from it which will give you the name of your plist file. Check the code below. Sorry for giving answer in Objective-C as I don't know Swift
NSDictionary *infoDict = [NSBundle mainBundle].infoDictionary;
NSString *plistFilePath = [[infoDict objectForKey:#"CFBundleInfoPlistURL"] absoluteString];
NSString *plistFileName = [[plistFilePath componentsSeparatedByString:#"/"] lastObject];
plistFileName is the required file name (with extension .plist)

App localization showing the key instead of the value in iOS

I've been using localization in my app, but for some reason, some of the strings (not all of them) won't translate, I see the key instead the value. I've tried to check if the app finds the localization files by doing this:
NSString *enPath = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
NSString *hePath = [[NSBundle mainBundle] pathForResource:#"he" ofType:#"lproj"];
NSString *ruPath = [[NSBundle mainBundle] pathForResource:#"ru" ofType:#"lproj"];
NSString *esPath = [[NSBundle mainBundle] pathForResource:#"es" ofType:#"lproj"];
NSString *frPath = [[NSBundle mainBundle] pathForResource:#"fr" ofType:#"lproj"];
NSString *arPath = [[NSBundle mainBundle] pathForResource:#"ar" ofType:#"lproj"];
And none of them is nil.
I've checked the name of the localization file and it's Localizable.strings as it should be.
Also checked if the key exists inside the Localizable.strings files and it does.
I've also tried:
Empty Cache
Cleaning all targets
Delete Derived Data folder
Restart
Reset simulator
Convert to UTF-16
Remove all localization files and recreate them.
Also tried to do everything that is in this question.
It's important to say that this is not just a Simulator/Cache problem. It's also showing on devices which download the app. (I have Enterprise account).
What more can I do in order to identify nor fix the problem?
So I found the problem, I guess who translated the Localizable.strings files for me is an asshole. In 4 places in my strings file there was a row as followed:
"KEY" ;= "Value"
This line cause some kind of a crash, but let the compiler to build successfully for some reason. That's why I couldn't find the bug, only when I decided to take the last Key and Value which are not translate and move them to the top of the Localizable.strings file. Then I was able to understand and see that the problem is somewhere in the middle of the file and the top Keys and Values are translated fine.
One thing that you can do catch these kind of errors is to make a copy of the strings file, change the extension to plist and try to open it in Xcode. If there is any problem in the strings file it will show in Xcode since the dictionary will contain only the keys till the point where there is an error. You can then do a Find operation and find the error until you are sure that all strings appear in the plist file. You can then rename the file back to .strings
If you specify table:nil, then NSBundle will try to fetch the localization from the default table (the one in SOMELANG.lproj/Localizable.strings). If you have the localization elsewhere, you should explicitly specify the table using table:#"File" (or use the NSLocalizedStringFromTable() macro in a similar manner:
NSString *value = NSLocalizedStringFromTable(#"key", #"File", nil);
Also,
Double check that the Localizable.strings file is being added to
Targets -> BuildPhases -> Copy Bundle Resources
It hadn't been added automatically for me.

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?
You can do it by performing some extra steps:
Duplicate [AppName]-Info.plist file by any name. Example: [AppName]-Info-dev.plist or [AppName]-Info-staging.plist etc
Map newly created .plist file in App's Target Settings. Get idea from following screenshot:
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"]
Project setting will automatically pick correct .plist file and give you required value.
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.
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.

Resources