Simplest way on iPhone to unzip downloaded file? - iphone

Goal: download a zipped file, unzip it, and save it in the iPhone app's Documents directory.
The following code makes use of the initWithGzippedData method that was added to NSData in the Molecule app found here:
http://www.sunsetlakesoftware.com/molecules
As adapted to my app:
NSString *sFolder = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *sFileName = [sFolder stringByAppendingPathComponent:#"MyFile.db"];
NSURL *oURL = [NSURL URLWithString: #"http://www.isystant.com/Files/MyFile.zip"];
NSData *oZipData = [NSData dataWithContentsOfURL: oURL];
NSData *oData = [[NSData alloc] initWithGzippedData:oZipData];
[oZipData release];
b = [oData writeToFile:sFileName atomically:NO];
NSLog(#"Unzip %i", b);
Result: A zip file is successfully downloaded. From it a new, supposedly unzipped file is created in the Documents directory with the desired name (MyFile.db) but it has zero bytes.
Anybody see the problem? Or else is there a simpler way to unzip a downloaded file than the one used in the Molecules app?

I think that your problem may be that you are attempting to gzip-deflate a Zip file. Those are two different compression algorithms.
I based the gzip-deflating code in Molecules on this NSData category (the code of which I've copied into this answer) provided by the contributors to the CocoaDev wiki. What you'll want to do is use their -zlibDeflate implementation, which should properly unzip a Zip file.
Unrelated to your problem, instead of using NSHomeDirectory() and appending a path component, the recommended approach for finding the documents directory is the following:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

You should make sure your file is never too big, as you are loading it fully into memory before the unzip starts.

Related

How can I load a .pod File from an URL in COCOS3D

I am making an app and I want to load some 3d models who are stored in a server. I can easily acces them in the browser with a link.
The idea of the app is that you have a menu and you can select which model do you want to see.
In cocos3D you can create a node from a pod file by simply executing:
[CC3PODResourceNode nodeFromResourceFile:podFileName];
Here you can find a full working sample.
If your pod file is available online, you could first download it locally:
NSURL *url = [NSURL URLWithString:YOUR_URL_HERE];
NSData *urlData = [NSData dataWithContentsOfURL:url];
[urlData writeToFile:filePath atomically:YES];
then pass filePath to nodeFromResourceFile.
filePath identifies a file residing in the user documents directory (one of the few places where you can write files to, under iOS):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"PODFILE.pod"];
If you want to save to a temporary directory, use:
NSTemporaryDirectory()
instead of documentsDirectory.

How to cache or store parsed xml file?

My app parses an xml file from my server, but I want to store parsed xml file and next start of my app, controller initially should load stored xml file, then controller should parse it again to check that there may be an update I did on xml file, if there is, new elements parsed should also be stored again.
I am referring to those app such as magazines, newspaper apps. When you open those kind of apps, it loads stored data that was downloaded previous session. Yet, after it loads, it starts to update the data, and it stores new update again.
Where do I start? What do you guys suggest?
Thanks in advance...
You can use CoreData or SQLite (use Objective-C wrapper FMDB https://github.com/ccgus/fmdb) to persist your XML. Then update the database everytime you see a unique id. Depends on how your XML data is.
It's actually quite easy to store to the documents directory. For example:
NSData *data; //this is your xml file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docs = [paths objectAtIndex:0];
NSString *filename = [NSString stringWithFormat:#"test.xml"];
NSString *path = [docs stringByAppendingPathComponent:filename];
[data writeToFile:path atomically:YES];
Then to retrieve it later, you can get the path like above, but retrieve the file instead of writing it:
NSData *data = [NSData dataWithContentsOfFile:path];
either CoreData or SQLite can do the trick

How to encrypt files in iPhone (PDF files)

i am developing a ebook reader and sending the ipa files to various people. The ipa files contains some PDF books. Is there any way in which i can encrypt the PDf files so that the user can see them only on device and not on PC... thanku
That's a general security question and the answer is: No. If one device (the iPhone) can decrypt the files without further data like a password or secret device key, another device (a desktop computer) can do this as well.
All you can do is obfuscate the files. That would keep people from simply unzipping the ipa and opening the PDFs. But any measure you take makes it only a little more difficult to access the files. There's no way to make it impossible for a skilled person to get at the data.
You could re-save the bundle pdf's using data protection, its not bulletproof but it makes is difficult to read the data (especially if the passcode is unknown), however it only works if the devices are pass-coded.
//There is probobly a quicker way to do this..ie..iterating the bundle programatically for pdf's
NSMutableArray * a = [[NSMutableArray alloc] init];
[a addObject:[NSString stringWithFormat:#"pdf1.pdf"]];
[a addObject:[NSString stringWithFormat:#"pdf2.pdf"]];
[a addObject:[NSString stringWithFormat:#"pdf3.pdf"]];
[self resaveFilesWithProtection:a];
[a release];
-(void)resaveFilesWithProtection:(NSArray*)fileNameArray
{
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * DocPath = [paths objectAtIndex:0];
NSFileManager *fileManager = [NSFileManager defaultManager];
for (NSString* s in fileNameArray) {
NSString * fullFilepath = [DocPath stringByAppendingPathComponent:s];//getting path to file
NSData *myData = [NSData dataWithContentsOfFile:filePath];//getting data out of old file
[fileManager removeItemAtPath:fullFilepath error:NULL];//deleting old file
NSError*er=nil;
[myData writeToFile:fullFilepath options:NSDataWritingFileProtectionComplete error:&er]; //saving back to disk with protection
}
NSLog(#"DONE");
}
You could also store the NSData in an SQLite database, or obscure the file extensions by re-saving them as .anything.
Edit:
If you don't want the user to be able to unzip the ipa and you think that extension obscuring isn't enough then your going to have to not put the pdf's in the bundle and pull them down from the network.

Problem when trying to play (with MPMoviePlayerViewController) a video/song that I downloaded and stored in a file

Here is my problem.
I have a MPMoviePlayerViewController that play some videos wich are on the web. That part works.
But in order to play them later, without internet connection, I store them on the phone with that piece of code
NSData * data = [NSData dataWithContentsOfURL:[self dataURL]];
NSArray * documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * baseDocumentPath = ([documentPaths count] > 0) ? [documentPaths objectAtIndex:0] : nil;
[data writeToFile:[baseDocumentPath stringByAppendingPathComponent:_itemId]
atomically:YES];
That part is ok, I can play the files on my iMac if i take them from the phone.
But after that when i do
NSArray * documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * baseDocumentPath = ([documentPaths count] > 0) ? [documentPaths objectAtIndex:0] : nil;
videoController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:[baseDocumentPath stringByAppendingPathComponent:file.itemId]]];
There is just a gray Window in the modal viewController. And i get no notifications from the player.
Any ideas?
Thanks.
I came across this very same problem today.
It seems that iOS won't load any media files that have the wrong file extension. IMHO this is pretty stupid behavior, as I'm storing my media files with random names (UUIDs).
A quick workaround was to use the following code to create a symlink to the original file and give it the correct extension. Now iOS will happily load the file.
// Create a symlink for iOS as it won't load any files with the wrong extension
NSString *fixedFileName = [fileName stringByAppendingString:#".mp4"];
[[NSFileManager defaultManager] createSymbolicLinkAtPath:fixedFileName
withDestinationPath:fileName error:NULL];
Hope that helps. We simply ignore the fact that an error occurred, in case the symlink already exists.
Someone found what causes the problem.
The file name has no extension (like .mp4) so the MPMovieController doesn't try to read it (that sounds crazy to me -_- ). If I manually had .mp4 to my video file. the app can read it... I'm gonna append the extension of each file to its name.
Thanks anyway :)

Compiling an XML file into a binary

I want to parse and XML file in an iPhone app without going to disk for it.
Can I do this? How? I have included a TXT helpfile in an app using MSVC.
I put the XML file in a Folder/Group named Resources in the project.
I have the XML file in the proj directory.
I right clicked on Resources folder and selected add -> Existing File.
I right-click on the XML file and select GetInfo.
There I have tried altering the Path Type {Absolute, Relative to Project , etc}
My program runs fine on the simulator when I use:
NSString * const DG_XmlRecipeFile = #"/Users/appleuser/Cocoa/iHungry6/Recipes.xml";
It seems to me it should also work with:
NSString * const DG_XmlRecipeFile = #"Recipes.xml";
If I set the Path Type correctly. It does not.
I am a first timer. Thanks for reading this , Mark
Xcode copies the project resources to the app bundle. You can access your file within your bundle as follows:
NSString *DG_XmlRecipeFile = [[NSBundle mainBundle] pathForResource:#"Recipes" ofType:#".xml"];
Files in the bundle are read-only. If you want to modify the file you will need to copy it somewhere that you can modify it. Your app's Documents directory works well for this.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDirectory = [[paths objectAtIndex:0] retain];
NSString *newFilePath = [docsDirectory stringByAppendingPathComponent:#"Recipes.xml"];
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtPath:DG_XmlRecipeFile toPath:newFilePath error:&error];
I don't think the path type you are referring to is the path to the resource within the app bundle that is produced. It is how the file should be referenced within the .proj file.