Archiving on iOS using NSKeyedArchiver - ios

I'd like my app to decompress and handle the contents of zip files.
I'm trying to do this the Cocoa way, without using external libraries, so I'm turning to NSKeyedUnarchiver and here's my code:
-(void) unarchive{
NSString *outputDirectory = [[NSHomeDirectory() stringByAppendingString:#"/Documents/"] stringByAppendingString:#"TheNewFolderName/"];
NSLog(#"Output Directory: %#", outputDirectory);
//MyArchive.zip is the filename of the zip file I placed in Xcode
NSData *theArchive = [NSKeyedUnarchiver unarchiveObjectWithFile:#"MyArchive.zip"];
NSError *error;
[theArchive writeToFile:outputDirectory options:NSDataWritingAtomic error:&error];
NSLog(#"theArchive is %#", theArchive);
And this prints "theArchive is null"! What am I doing wrong..?

NSKeyedArchiver and NSKeyedUnarchiver are used for archiving and unarchiving objects (NSObject subclasses, with certain restrictions) in your application. I'm afraid you cannot use it with zip files.

Related

Alternative for NSKeyedArchiver/ NSKeyedUnarchiver iOS

I have a dictionary that is written into a file after archiving it [NSKeyedArchiver archivedDataWithRootObject:dictionary] . I fetch the file contents and transferring the archived data to my peer devices through multipeer. Then, in my peer device, I am unarchiving the data using [NSKeyedUnarchiver unarchiveObjectWithData:data]. But, it is returning nil while unarchiving it, though data is present in it.
I am suspecting that the content is huge in file. Is there any alternative for NSkeyedArchiver/ NSKeyedUnarchiver ?
Code:
Archiving:
[[NSKeyedArchiver archivedDataWithRootObject:dictionary] writeToFile:fileAtPath options:NSDataWritingAtomic error:&error];
Transferring:
[NSData dataWithContentsOfFile:fileAtPath] ;
Unarchiving:
[NSKeyedUnarchiver unarchiveObjectWithData:data];
Try to write data like below,
[data writeToFile:filePath atomically:NO];
here data means [NSKeyedArchiver archivedDataWithRootObject:dictionary] your archived data.
otherwise archived and unarchived method is fine.
Hope this will help :)

iOS app out of memory handling

In an iOS application, I'm generating a 'voice recorder' functionality for continuous capturing of speech.
I use the following code for writing the speech in to a file.
//output speech
NSString *filePath = [root stringByAppendingPathComponent:#"output_speech.raw"];
if(![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSData data] writeToFile:filePath atomically:YES];
}
NSData *myData = [NSData dataWithBytes:ptrOut length:DataByteSize];
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
[handle truncateFileAtOffset:[handle seekToEndOfFile]];
[handle writeData:myData];
[handle closeFile];
My question is, in case the iOS device is going out of memory, how to handle the file writing situation?
There is a very similar question here:
iPhone: available disk space
I would check for the available disk space and appropriately determine whether to cancel the write and alert the user, or to just silently fail.
#Infinity James, I obtained the free space using the method mentioned in following link. [link] (http://www.linkedin.com/groups/How-detect-total-available-free-72283.S.217861544)

Unable to copy sqlite file from app bundle to documents directory: getting (Cocoa error 260)

I am trying to copy my sqlite file from app bundle into documents directory using the code below
-(id)init {
self = [super init];
if (self) {
// 1. Create a handle to the database file for UIManagedDocument
NSURL *docURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
docURL = [docURL URLByAppendingPathComponent:#"DefaultDatabase"];
self.document = [[UIManagedDocument alloc] initWithFileURL:docURL]; // URL of the location of document i.e. /documents directory
NSLog(#" URL document");
//set our document up for automatic migrations
if (self.document) {
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES],
NSInferMappingModelAutomaticallyOption, nil];
self.document.persistentStoreOptions = options;
// Register for Notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectsDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.document.managedObjectContext];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.document.managedObjectContext];
} else {
NSLog(#"The UIManaged Document could not be initialized");
}
// 2. Check if the persistent store file does not exists in case of first run
if (!([[NSFileManager defaultManager] fileExistsAtPath:[self.document.fileURL path]])) {
NSLog(#" persistent file not found trying to copy from app bbundle");
NSString *docFileName = [UIManagedDocument persistentStoreName];
NSString *docFilePath = [[NSBundle mainBundle] pathForResource:docFileName ofType:#"sqlite"];
**NSLog(#" doc file path = %#", docFilePath);**
if (docFilePath) { // found the database file in app bundle
NSLog(#" found file in bundle");
//Production: Copy from app bundle.
NSError *error = nil;
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *copyToPath = [searchPaths lastObject];
if([[NSFileManager defaultManager] copyItemAtPath:docFilePath toPath:copyToPath error:&error]){
NSLog(#"File successfully copied");
} else { // if could not locate the file
[[[UIAlertView alloc]initWithTitle:NSLocalizedString(#"error", nil) message: NSLocalizedString(#"failedcopydb", nil) delegate:nil cancelButtonTitle:NSLocalizedString(#"ok", nil) otherButtonTitles:nil] show];
NSLog(#"Error description-%# \n", [error localizedDescription]);
NSLog(#"Error reason-%#", [error localizedFailureReason]);
}
}
}
}
return self;
}
a) I created the .sqlite file using data loader app which uses UIManagedDcoument to add data to Core Data. The .sqlite file gets generated in documents directory.
b) I add the *.sqlite file to resources folder and add it to bundle. If I check the app bundle using Terminal..I see 'persistent store' and <app name.momd> file under the bundle directory. There is no file with extension .sqlite
c)But in my code above when I check for whether files exists in app bundle using line of code, it is successful. So file exists in bundle
NSString *file = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName];
d) But when try to copy, it fails and gives me (Cocca Error 206) meaning its unable to find the .sqlite file in the app bundle.
if([[NSFileManager defaultManager] copyItemAtPath:file toPath:copyToPath error:&error])
which is in line with the fact that I don't see .sqlite file under app bundle directory instead I see a persistent store and .momd files.
So where I am going wrong ?
EDIT
This is an explanation of how I am generating my mydata.sqlite file.
I am using Core Data and want to provide a pre-poulated database upon first launch of app to the user. So I used a data loader app to create .sqlite file for me. I am using UIManagedDocument for core data. After I run the app, I see a mydata.sqlite directory gets created under documents directory. The directory structure is as follows
/users//.../documents/mydata.sqlite/storeContent/persistenStore.
So basically instead of creating a file, it creates a directory with .sqlite extension and I see persistentStore file. So when I try to copy resources under app bundle in target under build phases..it adds the persistentStore and not .sqlite file.
My question whatever is described is correct and I am supposed to handle it differently in my code. If yes, what is that I am supposed to do to get handle on data store.
I thought .sqlite was a file and not a directory. Please guide
Thanks
This line does not actually check if the file exists:
NSString *file = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName];
All that does is build the path in file. If you want to check whether it exists, you need to use [NSFileManager fileExistsAtPath:]. Or you could go back to using pathForResource:ofType:, which was apparently correct when it returned nil.
You don't seem to be copying the file into the bundle at all. This is a problem with your Xcode project configuration.
I read up apple documentation on UIManagedDocument and here are the key points that I was going wrong on
Was handling UIManagedDocument incorrectly. When you initialize a managed document, you specify the URL for the document location. and not the document itself. If you need to add
You can perform additional customization by creating a subclass of UIManagedDocument i.e.
Override persistentStoreName to customize the name of the persistent store file inside the document’s file package.
cutting and pasting the example code for the right way to handle the data file
You create a managed document object using initWithFileURL:; if you want, you can then configure the document before you use its managed object context. Typically you might set the persistent store options, as illustrated in this example:
NSURL *docURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"FirstDocument"];
doc = [[UIManagedDocument alloc] initWithFileURL:docURL];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
doc.persistentStoreOptions = options;
**if ([[NSFileManager defaultManager] fileExistsAtPath:[docURL path]]**) {
[doc openWithCompletionHandler:^(BOOL success){
if (!success) {
// Handle the error.
}
}];
}
else {
[self addInitialData];
[doc saveToURL:docURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success){
if (!success) {
// Handle the error.
}
}];
}

It is allowed to use ssziparchive library in the App Store?

Due to cryptography export regulations, it is possible to use this library? or which one could I use to compress/decompress files?
I don't know if SSZipArchive is allowed to use in distributed apps, but the library I am using is Objective-Zip.
It can be easily integrated into any project.
Sample code for zipping:
// create a zip file for writing
ZipFile *zipFile= [[ZipFile alloc] initWithFileName:pathOfTheFileToBeZipped mode:ZipFileModeCreate];
// Add a file, write to its stream and close it
ZipWriteStream *stream1= [zipFile writeFileInZipWithName:#"abc.txt" fileDate:[NSDate dateWithTimeIntervalSinceNow:-86400.0] compressionLevel:ZipCompressionLevelBest];
NSString *text= #"abc";
[stream1 writeData:[text dataUsingEncoding:NSUTF8StringEncoding]];
[stream1 finishedWriting];
// Add another file, write to its stream and close it
ZipWriteStream *stream2= [zipFile writeFileInZipWithName:#"x/y/z/xyz.txt" compressionLevel:ZipCompressionLevelNone];
NSString *text2= #"XYZ";
[stream2 writeData:[text2 dataUsingEncoding:NSUTF8StringEncoding]];
[stream2 finishedWriting];
// Close the zip file
[zipFile close];
Sample code for unzipping:
// open the zip file for reading
ZipFile *unzipFile = [[ZipFile alloc] initWithFileName:pathOfTheFileToBeUnzipped mode:ZipFileModeUnzip];
// retrieve the info of the files inside
NSArray *infos= [unzipFile listFileInZipInfos];
// iterate over files
for (FileInZipInfo *info in infos) {
// locate the file in the zip
[unzipFile locateFileInZip:info.name];
// expand the file in memory
ZipReadStream *read= [unzipFile readCurrentFileInZip];
NSData *data = [read readDataOfLength:info.length];
[read finishedReading];
// construct the folder/file path structure
NSString *unzipPathFilename = [unzipPath stringByAppendingPathComponent:info.name];
NSString *unzipPathFoldername = [[unzipPathFilename stringByDeletingLastPathComponent] copy];
NSError *errorw;
// write the unzipped files, with some consistency checks
NSRange range = [unzipPathFoldername rangeOfString:#"__MACOSX"];
if (range.location == NSNotFound) {
if ([fileManager createDirectoryAtPath:unzipPathFoldername withIntermediateDirectories:YES attributes:nil error:&errorw]) {
if (![[unzipPathFilename pathExtension] isEqualToString:#""] && ![[[unzipPathFilename lastPathComponent] substringToIndex:1] isEqualToString:#"." ]) {
[data writeToFile:unzipPathFilename atomically:NO];
}
}
else {
NSLog(#"Directory Fail: %#", errorw);
}
}
}
// close the zip file
[unzipFile close];
Actually you are allowed to have encryption in iOS application.
You just have to submit application to NSA who you are and what kind of encryption do have in app.
Respond with your reg number usually comes in 30 minutes.
It is automatic or semi-automatic.
They just collect information about developers.
It is simpler then register as iOS developer.
My opinion:-
If you do not use encryption in ZIP library then you should submit any application.
Linker will remove that code after optimization.
That code is not used. But if you use encryption even that comes with iOS then you should apply.
e.g. UIWebView if it opens https:// URLs (e.g. Facebook) but if you use UIWebView to open non secure pages then you should not apply.

download file to Documents directory

I'm trying to copy a downloaded file to a specific folder in the app's documents directory but can't seem to get it working. The code I'm using is:
NSString *itemPathString = #"http://pathToFolder/folder/myFile.doc";
NSURL *myUrl = [NSURL URLWithString:itemPathString];
NSArray *paths = [fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *folderPath = [[paths objectAtIndex:0] URLByAppendingPathComponent:#"folder"];
NSURL *itemURL = [documentsPath URLByAppendingPathComponent:#"myFile.doc"];
// copy to documents directory asynchronously
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSFileManager *theFM = [[NSFileManager alloc] init];
NSError *error;
[theFM copyItemAtURL:myUrl toURL:itemURL error:&error];
}
});
I can retrieve the file OK but can't copy it. Can anyone tell me if there's anything wrong with the above code?
If downloading a file from a server, if it's a reasonably small file (e.g. measured in kb, not mb), you can use dataWithContentsOfURL. You can use that method to load the file into memory, and then use the NSData instance method writeToFile to save the file.
But, if it's a larger file, you will want to use NSURLConnection, which doesn't try to hold the whole file in memory, but rather writes it to the file system when appropriate. The trick here, though, is if you want to download multiple files, you either have to download them sequentially, or encapsulate the NSURLConnection and the NSOutputStream such that you can have separate copies of those for each simultaneous download.
I have uploaded a project, Download Manager that demonstrates what a NSURLConnection implementation might look like, but it's non-trivial. You might rather want to contemplate using an established, third-party library, such as ASIHTTPRequest or RestKit.
If you want to access a folder with a given name you should check if it exists and if not create it. That could quite easy be done like this:
NSString *folder = [documentsPath stringByAppendingPathComponent:folderName];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
if (![fileManager fileExistsAtPath:folder]) {
[fileManager createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:nil error:&error];
}
if (error != nil) {
NSLog(#"Some error: %#", error);
return;
}
EDIT
If you want to check if the folder was created properly on your device got to Organizer -> Devices -> [YourDevelopingDeviceWhereTheAppWasInstalled] -> Applications -> [YourApplication]
In the lower section you should at least see some folders like Documents. And if successful your created folders as well.
You need to create any intermediate directories prior to copying files. Check in the Simulator folder to see wether the "folder" directory is created in the applications Documents-folder.
Path to simulator is /Users/$username/Library/Application Support/iPhone Simulator/

Resources