How to find path to localized directory? - iphone

I need to distribute a directory containing html files and images with my app.
The app has support for different languages. I have created a directory for each language and then pick the right one based on current locale:
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString *path = [[NSBundle mainBundle] pathForResource:#"index"
ofType:#"html"
inDirectory:[language stringByAppendingPathExtension:#"html"];];
if (![[NSFileManager defaultManager] fileExistsAtPath:path])
{
// Fallback to english
path = [[NSBundle mainBundle] pathForResource:#"index"
ofType:#"html"
inDirectory:#"en.html"];
}
How can I better deal with this instead of having to do the above (which is a bit messy)?
I'm thinking perhaps using the xx.lproj directories for this somehow and putting a localized html directory in each xx.lproj directory and use NSBundle pathForResource to find the correct file. Couldn't get it to work though.

Using the xx.lproj folders with [NSBundle pathForResource:ofType:] is straight-forward.
You can add index.html to your Xcode's project file and then make it localizable from its "Get Info" window. Add another language like "de", and Xcode will copy index.html into the newly created de.lproj. If you remove that file, the app will fall back on the English version.
You can test it with logging:
NSLog([[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"]);

I too am unable to get this to work:
NSLog([[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"]);
But this does, using the standard Xcode language.lproj structure (e.g., en.lproj):
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString *htmlPath = [[NSBundle mainBundle] pathForResource:#"index"
ofType:#"html"
inDirectory:[language stringByAppendingPathExtension:#"lproj"]];
also removed extra ; in Martin's original question above ...

Extra tip: make sure that you remove the unlocalized versions of the files in your built product, otherwise pathForResource will find those files instead of the localized ones.

Why not the forLocalization way:
htmlFile = [[NSBundle mainBundle] pathForResource:#"myContent"
ofType:#"html"
inDirectory:#"de.lproj"
forLocalization:#"de"];
This works fine here with Xcode 5.1.1 and iOS 7.1
Add the generalization of #Martin Wickman (with the fallback) and everything should be great.

You could use this:
NSString *filename = [NSString stringWithFormat:#"html_%#.html", NSLocalizedString(#"abbr", #"")];

Related

How to get the files inside a folder in Supporting Files in xcode 4.2?

I have done the following steps.
1. Created an xcode project and created a folder named "Images" inside Supporting Files.
Dragged and dropped 4 images into it. Tried to access those files using the following code
NSString *pathString = [[NSBundle mainBundle] pathForResource:#"Images" ofType:nil];
NSArray *fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pathString error: nil];
NSLog(#"the fileList is %d",[fileList count]);
The count is always 0. What should I do now? There are files inside it and I am able to use it in imageviews.
So what is the mistake that I am making?
The Xcode does not generate any folders in your app bundle that corresponds to groups. Any resources added are present in Copy Bundle Resources in your target's Build Phases. You then access your resource under [[NSBundle mainBundle] bundlePath] path.
But if you want to count files under any folder then while adding that folder you must check the option :
"Create folder references for any added folders."
This will create folders and sub-folders in the same hierarchy as you add them.Then you can easily count them in the same manner you are doing above...
Otherwise the app bundle has all of your resources at one place not in any folder as you say "Images".Use following code :
NSArray *fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[[NSBundle mainBundle] resourcePath] error: nil];
NSLog(#"the fileList is %d",[fileList count]);
It will list all your resources.
Try this,
NSString *imagespath = [[NSBundle mainBundle] pathForResource:"yourimagename" ofType:#"png" inDirectory:#"images"];

How to use local xml saved in my project??

Hey the server which i am using is too slow to respond while using Maps, so i decide to ship my xml with the app and storing it locally in the app. How should i go about using it??
Thanks,
Use [[NSBundle mainBundle] pathForResource:#"myfile" ofType:#"xml"] to get the path. From there you can use NSData, NSString, or other classes to load the data.
You have not properly implemented the pathForResource:
NSString * string = [[NSBundle mainBundle] pathForResource"#"list-50.xml" ofType:#"xml"];
This is wrong.
So it should be
NSString * string = [[NSBundle mainBundle] pathForResource:#"list-50" ofType:#"xml"];

NSKeyedUnarchiver: iPhone .v. Simulator

I'm doing:
NSString *current_path = [[NSBundle mainBundle] bundlePath];
NSString *string_path = [NSString stringWithFormat:
#"%#/filedstring", current_path];
my_string_ = [[NSKeyedUnarchiver unarchiveObjectWithFile:string_path] retain];
The archived string is the text from a UITextField which we unarchive here. I've tried with and without current_path.
This all works fine when running in simulator (class member NSString *my_string_ is not nil) but when run on my iPhone my_string_ is nil.
Why is that?
Thanks for the quick responses all.
Adding to Jason Coco's answer, archive to and unarchive here:
NSString *library_path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
NSString *username_path = [library_path
stringByAppendingPathComponent:#"Caches/filedstring"];
You can't write into the main bundle on the phone, it's not allowed. That's why you don't find your archive there later. The simulator, since it actually runs on Mac OS X doesn't work this way, so it will actually write the file.
If you need to write something, you have to use one of the writeable paths available to your application. For more information, see the iOS Application Programming Guide / The File System. If you're going to do iOS Application Development, you should definitely read and understand this entire document.
As #middaparka says, there is probably something wrong with that file. Here's how I would debug this problem:
First step,
[[NSFileManager defaultManager] fileExistsAtPath: string_path];
Second step,
NSError *err;
NSString *tmp = [NSString stringWithContentsOfFile:string_path encoding:NSUTF8StringEncoding error:&err];
NSLog(#"Contents of string %#",tmp);
Once you've done those, you should have a much clearer idea why your NSKeyedUnarchiver is failing.
Also, check out NSString's stringByAppendingPathComponent: method.

iPhone Referring to Resources in Separate Directories

This question tells how one can create directories in your resources path. Once created, how does one reference these directories?
I have created an html directory with a structure for my internal pages, now I want to load the index.html file from the html directory. Thus, I'll need the file path to it.
I can just use:
NSString *filename = [[NSBundle mainBundle] pathForResource:#"index"
ofType:#"html"];
But what happens if there are two index.html files contained in the directory structure? Does it just find the first one? Can the referencing be more specific?
Little test project here that is not working if you want to take a look
If you have a path that is copied into your resource bundle, you also should reference the path when looking for the resource like:
NSString *filename = [[NSBundle mainBundle] pathForResource:#"html/index" ofType:#"html"];
EDIT:
You cannot apparently just include the directory in the resource name (I believe you can for other similar calls like "imageNamed:" on UIImage). The working call is:
NSString *helpFile = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html" inDirectory:#"html"];

How to display the current project version of my App to the user?

I would like to add the current version into the "about" section of my app.
As seen in this attached screenshot Apple offers versioning.
How do you display these settings in your app?
After further searching and testing, I found the solution myself.
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSLog(#"%i Keys: %#", [infoDictionary count],
[[infoDictionary allKeys] componentsJoinedByString: #" ,"]);
This snipplet gave me the following output:
20 Keys : NSBundleResolvedPath ,CFBundleVersion ,NSBundleInitialPath ,CFBundleIdentifier ,NSMainNibFile ,CFBundleIconFile ,CFBundleInfoPlistURL ,CFBundleExecutable ,DTSDKName ,UIStatusBarStyle ,CFBundleDevelopmentRegion ,DTPlatformName ,CFBundleInfoDictionaryVersion ,CFBundleSupportedPlatforms ,CFBundleExecutablePath ,CFBundleDisplayName ,LSRequiresIPhoneOS ,CFBundlePackageType ,CFBundleSignature ,CFBundleName
So the solution is as simple as:
NSString *version =[[[NSBundle mainBundle] infoDictionary] valueForKey:#"CFBundleVersion"];
However, this is not the Current Project Version as seen in the screenshot but the Bundle Version of the plist file.
Look into your Info.plist file which should have keys like CFBundleVersion and CFBundleShortVersionString
Those items in the Build Info are not available to your built app. They are placeholders that you could possibly pull into your app. What is in your app is anything that you place in, say, the Resources folder of your app, like any text files, or plists, or a nice picture of your versioning engineer.
Now, you could pull some of the items in the Build Info window into a info.plist, using special identifiers, such as ${VERSION_INFO_PREFIX} or other token. The tokens are available if you click on any of the items on the left hand side in the window you have included above. For example, click on the word "Current Project Version" and copy the token that you see at the bottom, "CURRENT_PROJECT_VERSION". Then go to your plist file, and add an entry. Give it any name you want or "Current Project Version". Paste in ${CURRENT_PROJECT_VERSION} on the right hand side. Now that value is available to you from your app, programmatically. Of course, someone now has to enter that value into the appropriate place either in the Build Info window or elsewhere. It might just be easier just to manage this and fields like this in the info.plist file. It's up to you how you'd like to handle these things.
Here is how I get version info out of my info.plist:
+ (NSString *) getAppVersionNumber;
{
NSString *myVersion,
*buildNum,
*versText;
myVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
buildNum = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleVersionKey];
if (myVersion) {
if (buildNum)
versText = [NSString stringWithFormat:#"Version: %# (%#)", myVersion, buildNum];
else
versText = [NSString stringWithFormat:#"Version: %#", myVersion];
}
else if (buildNum)
versText = [NSString stringWithFormat:#"Version: %#", buildNum];
NSLog(versText);
return versText;
}
NSString *currentVersion = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"CFBundleVersion"];
returns me always the same value(initial version) even if i change the version from the project settings but.
NSString * appVersionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleShortVersionString"];
does the trick.
For the lazy here is the swift version :
NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString")!
NSBundle.mainBundle.infoDictionary[#"CFBundleShortVersionString"];