I have an images group inside my xCode project. I am trying to get all the files from inside that folder into my app.
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtPath:#"/Images/" error:nil];
The above code always returns an empty array. I added the group in xCode 4.2 by right click and then "New Group".
The path you are using is probably not the correct one. /Images/ will refer to images in the root of your sandbox. If the images in your folder are added as a resource in your project, they will be stored in the Resources directory in your app, probably not even in the Images directory.
You also want to read the error to see what's wrong. That's what it is for:
NSErorr *error = nil;
NSArray *dirContents = [fm contentsOfDirectoryAtPath:#"/Images/" error:&error];
if (!dirContents) {
// inspect error
}
Use -[NSBundle resourcePath] to get a path to your applications resources directory.
Understand that your project structure is not equal to the location your resources will end up in your application. Resources are all copied to the Resources directory using your targets
Copy Bundle Resources buid phase.
If you really want to add certain files in a different directory in your resources directory add a new Copy Files phase, with a destination of Resources and a Subpath named with your folder, like Images
Then you can loop over those images using your file manager and the path you get using:
[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Images"];
Related
I'm trying to access a resource file I added in the app using relative pathing. I've read that I was supposed to use something like this :
NSBundle *mainBundle = [NSBundle mainBundle];
NSString *filePath = [[mainBundle resourcePath] stringByAppendingPathComponent:#"myFile.txt"];
fh = [NSFileHandle fileHandleForReadingAtPath:resourcePath];
Except this isn't working. With NSLog, I am able to confirm mainBundle isn't null and resourcePath returns something like this:
/Users/tom/Library/Developer/CoreSimulator/Devices/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX/data/Containers/Bundle/Application/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX/MyApp_Demo.app/myFile.txt
I've tried many things, such as adding the name of directory where the resource is located, that would be Ressources/myFile, but nothing is yielding any result.
I'm a total beginner with Objective-C but I have to tinker with legacy code and I have to deal with this, so any help is much appreciated.
Side note:
this is what the project structure looks like from Xcode:
Meanwhile, in Finder, the Ressources directory isn't inside the MyApp directory, rather they're on the same level inside the project directory. I wonder if that could be the problem.
Side note 2:
NSString *filePath2 = [[NSBundle mainBundle] pathForResource:#"myFile" ofType:#"txt"];
returns (null) after logging in the console.
It is best to use a simulator for this debugging process
Verification
You should check if the file or Resources folder is actually being copied to the right location or not. If you have added the Resources folder, than check it with the below code
NSString *resourcesFolderPath = [[NSBundle mainBundle] pathForResource:#"Resources" ofType:nil];
NSString *fullFilePath = [NSString pathWithComponents:#[demoToursPath,"filename.txt"]];
NSFileManager *manager=[NSFileManager defaultManager];
NSLog(#"Filepath: %#", fullFilePath);
NSLog(#"File Exist: %#", [manager fileExistsAtPath:fullFilePath]);
Once you have confirmed this, you can update your code to match the location and path it needs to be in order to access the file.
Additional Debug
You can also just print the Document Directory for the application and open a finder window and navigate there to see if the Resources folder is added at the right place (if it was added)
NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]
NSLog(#"Document Directory: %#", documentDir);
If you update the question with more info, I would be able to help you better
I have images.xcassets listed ounder copy bundle resources, and I did try to just state the file name by itself: MSB_big_icon , before trying to add the path within images.xcassets.
Can anybody tell me what I'm doing wrong?
NSString *path = [[NSBundle mainBundle]pathForResource:#"/Raymio_android_images/MSB_big_icon.imageset/MSB_big_icon" ofType:#"png"];
NSLog(#"path: %#", path);
MSBIcon *tilecon = [MSBIcon iconWithUIImage:[UIImage imageWithContentsOfFile:path] error:&error];
David Ansermot is right that xcassets is a much better approach and strongly preferred. If you can't use that (running on older versions of iOS for instance), still put everything in one directory and use imageNamed:. This has significant caching benefits over hand-loading the file.
An asset catalog (xcassets) is a (relatively) new, unified way of managing image resources. The images are no longer accessible as separate files on the disk. Instead, imageNamed: consults the asset catalog and fetches the correct asset.
Prior to asset catalogs (and still, for non-images), assets were stored in localized directories. All of your unlocalized assets would be put into a directory called Resources (no matter where those files might appear to be in your source tree, and no matter how those files might be arranged in your Xcode folders). Localized files would be stored in directories like English.lproj or French.lproj. When you make NSBundle calls to load MyImage, it looks at each localized directory in the order the user has configured, and if it cannot find it in any of those directories, it looks in Resources.
Now it is possible to store full directories as "a resource" by marking them as directory references in Xcode. In that case, the whole directory would be copied into Resources or the appropriate localized directory. In order to find files inside such a directory you can use the ...inDirectory: version of the NSBundle methods.
So most of the time, you want to just use imageNamed:, which is going to fetch things out of the asset catalog if available, and then search localized directories, and then look in Resources. If you need to find a non-image, or if for some reason you want the real path to the file, you can compute it like this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"MSB_big_icon" ofType:#"png"];
And if that resource were in a directory tree (because it was a directory reference in Xcode), you can access it like this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"MSB_big_icon"
ofType:#"png"
inDirectory:#"Raymio_android_images/MSB_big_icon.imageset"];
Here's a code exemple from one of my apps :
NSString *appKey = #"Applications__GENERIC";
NSString *path = [[NSBundle mainBundle] pathForResource:appKey ofType:#"plist"];
appData = [NSDictionary dictionaryWithContentsOfFile:path];
The "Applications__GENERIC.plist" is stored like this :
Other solutions :
Use the images.xcassets.
Then in your code to load an image, use the code :
UIImage *image = [UIImage imageNamed:#"MyImageWithoutExtension"];
Don't put any path or extension, only the image's name
Try using this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"MSB_big_icon" ofType:#"png" inDirectory:#"Raymio_android_images/MSB_big_icon.imageset"];
What you can also do to debug is to print out
[[NSBundle mainBundle] bundleURL]
Then navigate to that folder and see if the folder structure corresponds to the path you use.
I just struggled with this today, hope it works out for you too.
I've looked through the Apple documentation on this point and other questions here, but cannot find a means of getting a consistent path to the documents directory.
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *urls = [fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *directory = [urls lastObject];
This produces a different path each time due to one component.
Example:
file:///var/mobile/Containers/Data/Application/CA708CF5-0E1B-414D-A795-31A8BB884BA5/Documents
Next run:
file:///var/mobile/Containers/Data/Application/2C96E341-85EF-485D-AC19-F8844B0880C3/Documents
I realize I need some kind of relative path here but I cannot figure out how to get it. How can I get to the Documents directory consistently to both write and read a file my app will produce?
The path is determined on installation. Each time you run your app in the simulator, it will be removed and reinstalled. Hence the differernt path. So you don't need to worry about this.
I have an iOS app that stores the absolute path of files in a database and in generated html documents. I just recently updated my iPhone to iOS 8 and now when ever I run the app it seems that the app is installed in a different directory every re-compile. For example on the first build/run [[NSBundle mainBundle] bundlePath] returns something different on the next build/run. What is going on? Is this a new feature of Apple?
Update: A bug report was created
Code example:
If I run the following line over multiple build/runs then I will get a different result each time.
#define kOLD_PATH #"oldPath"
NSString* newPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSString* oldPath = [[NSUserDefaults standardUserDefaults] objectForKey:kOLD_PATH];
NSLog(#"New Path: %#", newPath);
NSLog(#"Old Path: %#", oldPath);
NSLog(#"Result: %#", [oldPath isEqualToString:newPath] ? #"Same" : #"Changed");
[[NSUserDefaults standardUserDefaults] setObject:newPath forKey:kOLD_PATH];
[[NSUserDefaults standardUserDefaults] synchronize];
The output looks like this over multiple runs
New Path: /var/mobile/Containers/Data/Application/4FFCE2CB-580D-409A-90CB-EF2B8A1FB653/Library
Old Path: /var/mobile/Containers/Data/Application/B038B2DA-F85D-4E18-A5F1-8635834EC454/Library
Result: Changed
Full Disclosure: In my app the user imports a web page (ePub) that has resources. The resources are stored with the web page. The web page also accesses resources that are part of the app bundle. To achieve this when I load the web page the base url is set to the directory the web page is in and the bundle resources are accessed via absolute file paths. Now that file paths change on every update this is broken. I tried creating symbolic links to the bundle resources but that also fails un subsequent updates.
In iOS 8, The file system layout of app containers has changed. Applications and their content are no longer stored in one root directory.
From the iOS 8 Release Notes:
The file system layout of app containers has changed on disk. Rather
than relying on hard-coded directory structure, use the
NSSearchPathForDirectoriesInDomains function or the
URLForDirectory:inDomain:appropriateForURL:create:error: method of the
NSFileManager class. See Accessing Files and Directories in File
System Programming Guide.
This is not a bug. Make sure you use the recommended APIs (from the above quote) and you won't have a problem.
So, If you are trying to access a bundled resource you added to the project, you would use:
[[NSBundle mainBundle] pathForResource:#"resourceName" ofType:#"extension"];
But if you want to use something that you put in the documents directory, you would use:
[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:#"resourceName.extension"];
Refer Technical Note 2406 by Apple
The breaking change is
Beginning in iOS 8, the Documents and Library directories are no
longer siblings of your application's bundle.
Don't store full path/URL to your documents. Store the file name and always generate full path/URL with recommended approach.
Get the DocumentsDirectory URL
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory {
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
Then you get path out of url and append the file name to generate full path.
don't know if you solved your problem, but this link is possible the answer.
https://developer.apple.com/library/prerelease/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html#//apple_ref/doc/uid/TP40010672-CH3-SW10
Locating Files Using Bookmarks
A few lines before this section in the page is this text:
"Important: Although they are safe to use while your app is running, file reference URLs are not safe to store and reuse between launches of your app because a file’s ID may change if the system is rebooted. If you want to store the location of a file persistently between launches of your app, create a bookmark as described in Locating Files Using Bookmarks."
Good bye.
I think the different path for each build and run is the intended way of things happening in iOS simulator. It is not an issue.
/var/mobile/Containers/Data/Application/4FFCE2CB-580D-409A-90CB-EF2B8A1FB653/Library
/var/mobile/Containers/Data/Application/B038B2DA-F85D-4E18-A5F1-8635834EC454/Library
I found even if you use the recommended way
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
}
The results are same. Different path for each build & run.
My project has a folder (not group) named "data" which contains many subfolders, each of which contains a set of files.
My question is, how do I grab a URL (or path) reference to the "data" folder? How about to its subfolders? I'm sure it's a fairly simple task, so forgive my ignorance, but I've never used folder refs in a project so I'm not familiar with the code. I did look over the NSFileManager ref but I'm fuzzy on how to make use of it.
Thanks in advance.
NSBundle can give you your absolute path in the system.
NSString (or NSURL) has methods for working with paths.
NSFileManager allows you to move, copy, delete (…) files.
This is how you get path to your Data directory:
NSString *dataDir = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"Data"];
// "/var/private/Application/.../YourApp.app/Data"
Now you just append multiple directory names to dataDir using the same method above and you should get any path you want.
In case you don't know the exact path and you want to scan the directory, you will have to use
:
NSArray *dataDirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dataDir error:nil]
// "file1.data", "file2.data", ...
Then it's all about appending path components.