iOS - writeToFile from NSData completion handler - ios

I am getting bytes of a file from a webservice, my goal is to get these bytes, put them into NSData and then write into a file, once the writeToFile is 100% complete, I would then display the file (its a PDF, can be up to 6 pages) in QLPreviewController.
I am able to get the bytes from the webservice and put them into NSData like so:
NSArray *byteArray = [dataSource.areaData GetPDFFileData:[NSString stringWithFormat:#"%#",encodedUrlStr]];
NSData *data;
for (NSDictionary *dict in byteArray) {
NSString *base64 = dict[#"data"];
data = [[NSData alloc] initWithBase64EncodedString:base64 options:0];
}
Now I am trying to write the file like so:
if (data)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,PDFFile];
[data writeToFile:filePath atomically:YES];
}
Now what I am trying to do is have some sort of completion handler so nothing else happens until the file is completely written. What would be the best way to do this?
I am also starting to look into NSFileHandle, would that be a better solution?

use NSFilePresenter to monitor for changes to the file
and use a NSFileCoordinator to write to it
in mock code:
NSFilePresenter *p = [NSFilePresenter presenterWithFile:f];
[p signupForChangeNotificationsOrSetDelegateOrWhatever....];
//write
NSFileCoordinator *c = [NSFileCoordinator coordinatorWithPresenter:p];
[c coordinate writeData....]
... called back by p cause we changed

No need to worry about completion if this runs in main queue it's serial , but it's better to save in background queue and read content to verify and present in main Queue
DispatchQueue.global(qos: .background).async {
print("This is run on the background queue")
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSLog(#"not exists");
NSData *fileContents = [#"" dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:fileContents
attributes:nil];
}
else
{
NSLog(#"exists");
}
NSFileHandle *myHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
[myHandle seekToEndOfFile];
NSString*strTowrite=[NSString stringWithFormat:#"%# || %# --- %# \r\n",dt,acion,name];
[myHandle writeabilityHandler];
[myHandle writeData:[strTowrite dataUsingEncoding:NSUTF8StringEncoding]];
DispatchQueue.main.async {
NSString*content=[[NSString alloc] initWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:&error];
if(content)
{
NSLog(#"content is : %# Err : %#",content,error);
// [self sendfile];
}
else
{
NSLog(#"Failed to store the file. Error = %#", error);
}
}
}

Related

Storing Downloaded PDF To Documents Directory Not Working

I have the following code, I don't want to get into why I am doing it this way, but for some reason this is not working. The stringURL is working fine, it gets data back, but fails to write to the document directory. This is the first time I'm working with files, and have been pulling my hair out trying to get this to work. Please could someone point me in the right direction?
+ (void) downloadAndStorePDFFromURLWithString: (NSString *) stringURL andFileID: (NSString *) fileID andTitle: (NSString *) title;
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *pdfData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: stringURL]];
dispatch_async(dispatch_get_main_queue(), ^(void) {
//STORE THE DATA LOCALLY AS A PDF FILE
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
documentsDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat: #"%#/%#", fileID, title]];
//GET LOCAL FILE PATH OF DOWNLOADED PDF
//NSLog(#"SUCCESSFULLY DOWNLOADED DOCUMENT FOR FILE: %# WILL BE STORED AT %#", fileID, documentsDirectory);
BOOL success = [pdfData writeToFile: documentsDirectory atomically: YES];
NSLog(success ? #"Yes" : #"No");
//TELL TABLEVIEW TO RELOAD
//[[NSNotificationCenter defaultCenter] postNotificationName: #"DocumentDownloaded" object: nil];
//SAVE FILEPATH URL IN NSUSERDEFAULTS
//[PDFDownloadManager addURLToListOfSavedPDFs: [PDFDownloadManager filePath: fileID andTitle: title] andFileID: fileID];
});
});
}
You are attempting to write the file to a subfolder of the Documents folder. This is failing because the subfolder doesn't exist. You need to create the folder before you can write to it.
You should also clean up the code a bit. And use the better NSData method to write the file.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *folder = [documentsDirectory stringByAppendingPathComponent:fileID];
[[NSFileManager defaultManager] createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:nil error:nil];
NSString *filePath = [folder stringByAppendingPathComponent:title];
NSError *error = nil;
BOOL success = [pdfData writeToFile:filePath options: NSDataWritingAtomic error:&error];
if (!success) {
NSLog(#"Error writing file to %#: %#", filePath, error);
}

writeToFile fails with error = null

I have an app running in the XCode simulator (v6.4); this is the pertinent code:
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
// read the file back into databuffer...
NSFileHandle *readFile = [NSFileHandle fileHandleForReadingAtPath:[documentsPath stringByAppendingPathComponent: #"Backup.txt"]];
NSData *databuffer = [readFile readDataToEndOfFile];
[readFile closeFile];
// compress the file
NSData *compressedData = [databuffer gzippedData] ;
// Write to disk
NSString *outputPath = [NSString stringWithFormat:#"%#/%#%#.zip", documentsPath, venueName, strDate];
_BackupFilename = fileName; // save for upload
NSFileHandle *outputFile = [NSFileHandle fileHandleForWritingAtPath:outputPath];
NSError *error = nil;
// write the data for the backup file
BOOL success = [compressedData writeToFile: outputPath options: NSDataWritingAtomic error: &error];
if (error == nil && success == YES) {
NSLog(#"Success at: %#",outputPath);
}
else {
NSLog(#"Failed to store. Error: %#",error);
}
[outputFile closeFile];
I'm trying to create a backup of a file by taking the file, compressing it and then writing it out. I get an error Failed to store. Error: (null)); why is it failing without returning the error code?
There are quite a few things wrong here. To start. change your if statement to:
if (success) {
Never explicitly compare a BOOL value to YES or NO.
You also never use outputFile so remove that code. It may interfere with the call to writeToFile:.
And there's no point in using the file handle to read the data. Just use NSData dataWithContentsOfFile:.
And don't build paths using stringWithFormat:.
Overall, I would write your code as:
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
// read the file back into databuffer...
NSString *dataPath = [documentsPath stringByAppendingPathComponent:#"Backup.txt"]];
NSData *databuffer = [NSData dataWithContentsOfFile:dataPath];
// compress the file
NSData *compressedData = [databuffer gzippedData];
// Write to disk
NSString *outputName = [NSString stringWithFormat:#"%#%#.zip", venueName, strDate];
NSString *outputPath = [documentsPath stringByAppendingPathComponent:outputName];
// write the data for the backup file
NSError *error = nil;
BOOL success = [compressedData writeToFile:outputPath options:NSDataWritingAtomic error:&error];
if (success) {
NSLog(#"Success at: %#",outputPath);
} else {
NSLog(#"Failed to store. Error: %#",error);
}
Since success is still NO and error is still nil, then most likely this means that compressedData is nil. This probably means that databuffer is nil which means that there is no file named Backup.txt (case matters) in the Documents folder.

Download folder from server to local iPad / iPhone

I want to create an application that automatically downloads a folder from my own server and store it locally on the iPad/iPhone. How can i accomplish this and where does the folder get stored on the local iDevice ? Therefore how will i access it afterwards? Many thanks for your help
The best way to do so was actually putting all the pictures for example in one zip file, downloading it and unzipping it on the real device using this code :
dispatch_queue_t queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:#"someDirectory/newPics.zip"];
NSError *error = nil;
// 2
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
if(!error)
{
// 3
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *zipPath = [path stringByAppendingPathComponent:#"newPics.zip"];
[data writeToFile:zipPath options:0 error:&error];
if(!error)
{
ZipArchive *za = [[ZipArchive alloc] init];
// 1
if ([za UnzipOpenFile: zipPath]) {
// 2
BOOL ret = [za UnzipFileTo: path overWrite: YES];
if (NO == ret){} [za UnzipCloseFile];
// 3
NSString *imageFilePath = [path stringByAppendingPathComponent:#"newPics/pic1.png"];
//[self removeImage:zipPath];
[[NSFileManager defaultManager] removeItemAtPath:zipPath error: &error];
dispatch_async(dispatch_get_main_queue(), ^{
NSURL *fileURL = [NSURL fileURLWithPath:imageFilePath];
[_webV loadRequest:[NSURLRequest requestWithURL:fileURL]];
});
}
}
else
{
NSLog(#"Error saving file %#",error);
}
}
else
{
NSLog(#"Error downloading zip file: %#", error);
}
});
It's the best way to do it , fast and reliable.

Unable to create Plist of NSStrings [duplicate]

This question already has answers here:
Write to string in plist dictionary
(2 answers)
Closed 8 years ago.
hi am trying to create a plist of my calculations but it turns out the list is empty....pls help here is my code.is there any othe way to write strings to a file other than this...
-(float)calculate
{
if(!self.operatorLabel.text.length)
return self.operand2;
float result=0;
switch([self.operatorLabel.text characterAtIndex:0])
{
case '+':result=self.self.operand1+self.operand2;
break;
case '-':result=self.operand1-self.operand2;
break;
case '*':result=self.operand1*self.operand2;
break;
case '/':if(self.operand2==0)
divideByZeroFlag=true;
else
result=self.operand1/self.operand2;
break;
default:self.operand1=0;
result=self.operand2;
break;
}
if(!divideByZeroFlag&&self.operand1!=0)
{
NSString *data=[NSString stringWithFormat:#"%g%#%g=%g",self.operand1,self.operatorLabel.text,self.operand2,result];
NSMutableDictionary *dat;
[dat setObject:data forKey:#"nthing"];
[dat writeToFile:#"memory.plist" atomically:YES];
}
return result;
}
1 - You must initialize dat.
2 - [NSDictionary writeToFile] expects full path, not just the name.
EDIT:
To create a path, do this:
+ (NSString*) createFullFilePath:(NSString *)fileName
{
//Look at documents for existing file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#", fileName]];
NSFileManager* fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:path])
{
NSError *error;
[fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
if (error)
NSLog (#"%#", [error localizedDescription]);
}
return path;
}
Once done, write to that path using following code (note that this initializes dictionary too (check NSLogs to see what you are doing yields results):
NSString * path = [self createFullFilePath:#"memory.plist"];
NSLog (#"#%", path);
NSMutableDictionary *dat = [NSMutableDictionary dictionary];
NSLog (#"#%", dat);
[dat setObject:data forKey:#"nthing"];
[dat writeToFile:path atomically:YES];
Why a plist file? A plist is nice for arrays and dictionaries, but I see no reason to store a simple string in a plist.
How about...
NSString *data = ...
NSError *error = nil;
[data writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];

NSData writeToFile not working

I cant seem to get nsdata to write to a file. Any ideas what i may be doing wrong. Thanks in advance.
NSString* filename = #"myfile.txt";
NSString *applicationDocumentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *storePath = [applicationDocumentsDir stringByAppendingPathComponent:filename];
if ([fileManager fileExistsAtPath:applicationDocumentsDir])
NSLog(#"applicationDocumentsDir exists"); // verifies directory exist
NSData *data = [NSData dataWithContentsOfURL:URL];
if (data) {
NSString *content = [[NSString alloc] initWithBytes:[data bytes]
length:[data length] encoding: NSUTF8StringEncoding];
NSLog(#"%#", content); // verifies data was downloaded correctly
NSError* error;
[data writeToFile:storePath options:NSDataWritingAtomic error:&error];
if(error != nil)
NSLog(#"write error %#", error);
}
I keep getting the error
"The operation couldn’t be completed. No such file or directory"
Try
NSString *storePath = [applicationDocumentsDir stringByAppendingPathComponent:#"myfile.txt"];
And
if ([[NSFileManager defaultManager] fileExistsAtPath:storePath])
NSLog(#"applicationDocumentsDir exists");
To get more information, you can use
writeToFile:options:error:
instead of
writeToFile:atomically:
but you need to create all the subdirectories in the path prior to doing the write. Like this:
// if the directory does not exist, create it...
if ( [fileManager fileExistsAtPath:dir_path] == NO ) {
if ( [fileManager createDirectoryAtPath:dir_path withIntermediateDirectories:NO attributes:NULL error:&error] == NO ) {
NSLog(#"createDirectoryAtPath failed %#", error);
}
}

Resources