iPhone File system operation questions - iphone

I want to download files from remote to temp folder
the folder on remote like:
http://remoteserver.com/abc/def/file1.txt
http://remoteserver.com/abc/file2.png
http://remoteserver.com/abc/pla/mnb/file3.html
and the folder structure will like:
tmpefolder/abc/def/file1.txt
tmpefolder/abc/file2.png
tmpefolder/abc/pla/mnb/file3.html
And then after download, will move files to permanent folder like and keep same folder structure
permanentfolder/abc/def/file1.txt
permanentfolder/abc/file2.png
permanentfolder/abc/pla/mnb/file3.html
finally remove all files in tempfolder
So my questions are:
What the best way to download multi files from server? (Better to show ASIHTTPRequest, it is ok to show me other way)
Easy way to create the whole structure of folders? Do I have to split folder path by "/" and check every level path exist and create it?
How to copy whole temp folder content to permanent folder? Is it possible to do this with one operation like copy on OS X?
Also, like remove operation on OS X, remove temp folder with one shot?
Thank you!

1/ You'd better use AFNetwork. ASIHTTPRequest is growing old, no longer maintained. AFNetwork is more modern, and works with blocks (“hmmm, blocks”, like Homer would say). There are plenty of examples around here, just search.
Specifically, AFNetwork allows you to put download operations in a NSOperationQueue, that you can handle at your will, let's say, to download 35 files in parallel, with a maximum of 4 running downloads at the same time, and report to you when everything's done.
2, 3, 4/ Take a look at the reference for NSFileManager. All you need is there.

create .zip of all your file use following code to download .zip form server.
this will create your folder in NSTemporaryDirectory.
NSString *filePath = [NSString stringWithFormat:#"%#/FILENAME.zip",NSTemporaryDirectory()];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:[NSData dataWithContentsOfURL:[NSURL URLWithString:[obj valueForKey:#"zip_path"]]] attributes:nil];
after download move folder to documentDirectory.
[[NSFileManager defaultManager] moveItemAtPath:filePath toPath:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] error:nil];
following code is remove files form temp
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];

Related

NSFileManager - Copying Files at Startup

I need to copy a few sample files from my app's resource folder and place them in my app's document folder. I came up with the attached code, it compiles fine but it doesn't work. All the directories I refer to do exist. I'm not quite sure what I am doing wrong, could someone point me in the right direction please?
NSFileManager*manager = [NSFileManager defaultManager];
NSString*dirToCopyTo = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString*path = [[NSBundle mainBundle] resourcePath];
NSString*dirToCopyFrom = [path stringByAppendingPathComponent:#"Samples"];
NSError*error;
NSArray*files = [manager contentsOfDirectoryAtPath:dirToCopyFrom error:nil];
for (NSString *file in files)
{
[manager copyItemAtPath:[dirToCopyFrom stringByAppendingPathComponent:file] toPath:dirToCopyTo error:&error];
if (error)
{
NSLog(#"%#",[error localizedDescription]);
}
}
EDIT: I just edited the code the way it should be. Now however there's another problem:
2010-05-15 13:31:31.787 WriteIt
Mobile[4587:207] DAMutableDictionary.h
2010-05-15 13:31:31.795 WriteIt
Mobile[4587:207] FileManager
Error:Operation could not be
completed. File exists
EDIT : I have fixed the issue by telling NSFileManager the names of the copied files's destinations.
[manager copyItemAtPath:[dirToCopyFrom stringByAppendingPathComponent:file] toPath:[dirToCopyTo stringByAppendingPathComponent:file] error:&error];
I think the problem is in this line:
NSArray*files = [manager contentsOfDirectoryAtPath:dirToCopyTo error:nil];
You are listing files in a destination directory instead of the source. Change it to something like:
NSArray*files = [manager contentsOfDirectoryAtPath:dirToCopyFrom error:nil];
And you should be fine.
I think the problem is that yo are reading the files to copy from dirToCopyTo and I think you meant dirToCopyFrom
Also to get the documents directory you should be using NSDocumentDirectory with - (NSArray *)URLsForDirectory:(NSSearchPathDirectory)directory inDomains:(NSSearchPathDomainMask)domainMask
Please note that lengthy operation on startup must be avoided:
Not a good User Experience (delay and croppy behavior)
Watchdog in iOS can kill your app as if it were stuck.
So perform copy in a secondary thread (or operation... or whatever uses a different execution path).
Another problem will arise if You need data to populate your UI: in that case:
Disable UI elements
Start an async / threaded operation
In the completion call back of copying (via a notification, a protocol.. or other means)
notify to the UI interface it can start fetching data.
For example we copy a ZIP file and decompress it, but it takes some time so we had to put it in a timer procedure that will trigger UI when done.
If You need an example, ket me know.
PS:
Copying using ZIP file is MORE efficient as:
Only call to file system
Far less bytes to copy
The bad news: you must use a routine do decompress zip file, but you can find them on the web.
Decompressing Zip files should be more efficient as these calls are written in straight C, and not in Cocoa with all the overhead.
[manager copyItemAtPath:[dirToCopyFrom stringByAppendingPathComponent:file] toPath:dirToCopyTo error:&error];
The destination path is the path you want the copy to have, including its filename. You cannot pass the path to a directory expecting NSFileManager to fill in the name of the source file; it will not do this.
The documentation says that the destination path must not describe anything that exists:
… dstPath must not exist prior to the operation.
In your case, it's the path to the destination directory, so it does exist, which is why the copy fails.
You need to make it a path to the destination file by appending the desired filename to it. Then it will not exist (if not previously copied), so the copy will succeed.

iPhone SDK: subFolders inside the main bundle

in the current project I have a number of folders, with subfolders, and these contain images: 01.png, 02.png.
Folder1/FolderA/f1.png
Folder1/FolderB/F1.png
When I compile the app, I looked inside the the .app and noticed that all the images are placed in the top level, with no sub-folders.
So clearly when trying to load the image this doesn't work:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"f1"
ofType:#"png"
inDirectory:#"Folder1/FolderA"];
But even more strangely, when loading image "f1", the image actually loads "F1"
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"f1.png"]];
Anyone have ideas on how to get around this problem?
Is there a compile option to create the folders in the app bundle?
TIA.
To create the subfolders inside the .app bundle, you should check the option "Create folder references for any added folders" rather than the default "Recursively create groups for any added folders"
Now in XCode, your imported folder appears blue rather than yellow. Build and go and you should see folders in your .app file.
First of all the folders you create in Xcode are simply organizational structures without any counterpart on the filesystem level. In other words all folders except for the "Classes" folder gets flatten out at the filesystem level. Therefore, even if you put your image file in the following location within xcode, it would still exist at the top-level in the filesystem: f1/f2/f3/f4/f5/image.png. Thus, in pathForResource method, you should not include the inDirectory argument.
As for the second issue, mac osx doesn't recognize case-sensitive file names. Therefore, f1 and F1 are equivalent to mac osx and will reference the same file. You can easily see this by executing the following 2 commands at a terminal session:
touch f
touch F
you'll notice that only 1 file exists after this: namely f. If you reverse the 2 commands, you still get one file, but it is named F.
ennuikiller is right. I think you can organize your images via Finder in subfolder and then refresh the image location in XCode by right clicking your image and selecting "Get Info" option. Then set the new directory.
Cheers.
Just wanted to add to Mugunth's answer, to follow up on part of the original question which was trying to use:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"f1" ofType:#"png" inDirectory:#"Folder1/FolderA"];
... to access files added using folder references. The above 'pathForResouce' call will work on simulators but not on actual iPhones (nil will be returned), because they don't seem to recognize subfolders in the 'inDirectory' part of the method. They do recognize them in the 'pathForResource' part, though. So the way of rephrasing the above so that it works on iPhones is:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"FolderA/f1" ofType:#"png" inDirectory:#"Folder1"];
I've followed your answer but it seams all files are stored flat on the root. If I use the code below to get the full path
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef url = CFBundleCopyResourceURL(mainBundle, CFSTR("About"), CFSTR("png"), NULL);
UInt8 filePath[PATH_MAX];
CFURLGetFileSystemRepresentation(url, true, filePath, sizeof(filePath));
I get as a result: /Users/iosif/Library/Application Support/iPhone Simulator/6.1/Applications/33FB4F79-999C-4455-A092-906A75226CDB/Arithmetics.app/About.png

Is possible to read plist from application bundle and documents folder at the same time?

Is it possible?to read from my local bundle and at the same time also read from documents folder into UItableview?
thanks thanks
yes.simultaneously
No — as in the iPhone isn't multicore, you can't have "simultaneous" :p
Yes — as in you can open multiple files in the same period of time. There's no conflicts as long as the files are different (if the files are the same then it depends on how others are using and locking the file etc.)
on viewDidLoad or some similar event when you would be populating your table data, you would simply just aggregate the two files together... that is you are are likely populating an array or dictionary with the contents of the file in question... so use the mutable version of array/dictionary, initialize it empty, then read in the first file from whatever location you choose, populating into your mutable array/dictionary, then do the the same for the next file. after you are done, reloadData as you normally would as if you had read form one file.
As far as simultaneous goes, technically no. However, one could have two different active threads each one reading required files and parsing the data.
Regarding the files you want to access...
Here is a quick and dirty method I use in one project (which I just happen to be working on at the moment):
NSFileManager* fileManager = [NSFileManager defaultManager];
NSError* error;
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
self.documentsDirectory = [paths objectAtIndex:0];
self.blahDBPath = [self.documentsDirectory stringByAppendingPathComponent: #"blah.db"];
NSLog(#"Mainbundle Resourcepath: %#", [[NSBundle mainBundle] resourcePath]);
NSString* defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"blah.db"];
NSLog(#"Default DB Path: %#", defaultDBPath);
success = [fileManager copyItemAtPath:defaultDBPath toPath:self.blahDBPath error:&error];
if (!success) {
NSAssert1(0, #"blah blah blah '%#'.", [error localizedDescription]);
}
It is ugly but effective. I'll rewrite it when I refactor the application.
The point here is that I ask the operating system for the path to certain directories. Then I add file names or subdirectories as required. This allows the operating system to manage paths (like in the simulator where each successive build gets a new unique id as part of its path) and I just worry about the final directories and file names in the application.
One I have the paths, I copy the required file from the bundle directory and put them somewhere, the Documents directory in this case. Then I can do whatever I need to with them.
If I just wanted to access them as they are in the bundle directory, then I I just refer to them by using [[NSBundle mainBundle] resourcePath].
I think something along the lines of the above snippet is what you are looking for.
-isdi-

Find all files in iphone project

I'm curious if there's a way to search for files in the iPhone's directory ( the location is irrelevant).
I am wanting to load in addresses from text files. The thing is additional files may be added and I want to dynamically be able to find the files and load in the data without hardcoding the file names to load in.
Thanks a bunch!
You want:
NSError *error;
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:myPath error:&error]
It returns an array of NSString objects for each file in the given directory.

Why can't I write files to my app's Document directory?

I have found several snippets of code describing how to write data to a user's application Documents folder. However, when I try this out in the iPhone simulator, no files get created. I called
[NSFileManager isWritbleAtPath:<my document folder>]
and it returned 0 (false). Do I need to make this folder explicitly writable, and if so, how do I do it?
The iPhone simulator should be able to write to the entire disk. My app routinely dumps test files to the root level of my boot volume (using [NSData's writeToPath:#"/test.jpg" atomically:NO]).
Are you sure that you've correctly determined the path to the documents folder? You need to expand the tilde in the path. Here's the code my app uses to put things in the documents folder. I don't think there's any more setup involved!
brushesDir = [[#"~/Documents/BrushPacks/" stringByExpandingTildeInPath] retain];
// create brush packs folder if it does not exist
if (![[NSFileManager defaultManager] fileExistsAtPath: brushesDir])
[[NSFileManager defaultManager] createDirectoryAtPath:brushesDir withIntermediateDirectories:YES attributes:nil error:nil];
NSLog(#"writable: %d", [[NSFileManager defaultManager] isWritableFileAtPath:NSHomeDirectory()]);
This prints 1 on the console.
Did you mean to call the method isWritableAtPath or isWritableFileAtPath ? And did you mean to call it on the class itself, or on a (default) instance of it?
Thanks for the pointers. So after a toiling through a few documents, I found the thing I was doing wrong: trying to save an NSArray that wasn't composed of basic datatypes such as NSDictionary, NSArray, or NSString. I was trying to save an array of MPMediaItems (from the MediaKit Framework in SDK 3.0+).
I had a trivial issue with the file writing to NSBundle. I had a requirement where a text file needs to be updated with the server as soon as app launches and it worked well with the simulator but not with the device. I later found out that we don't have write permission with NSBundle. Copying the file into Documents directory from NSBundle and using for my purpose solved my problem. I use :
[myPlistData writeToFile:fileName atomically:NO];