Objective-C dataWithContentsOfURL returns nill - ios

I have this code here:
NSString *stringURL = #"\\\\SERVER\\FOLDER\\FOLDER\\FTP\\ANC\\ANC.pdf";
NSURL *url = [NSURL fileURLWithPath:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
but urlData returned nill
I have tried datawithcontentsoffile but I got a warning when using the url variable with it.
When I goto this url file://SERVER/FOLDER/FOLDER/FTP/ANC.pdf in Windows, it opens the file, but on mac it does not not.
I have also tried the following:
NSString *stringURL = #"file://SERVER/FOLDER/FOLDER/FTP/ANC.pdf";
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:stringURL]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle response
}] resume];
// NSURL *url = [NSURL URLWithString:stringURL];
// NSData *urlData = [NSData dataWithContentsOfURL:url];
but get these errors:
NSErrorFallingURLStringKey
NSErrorFallingURLKey
NSLocalizedDescription
NSUnderlyingError
UPDATE I am able to get url not return nill with the following:
NSURL *url = [NSURL fileURLWithPath:#"file:///server/FOLDER/FOLDER/FTP/ANC/ANC.pdf"];
however, this returns nill:
NSData *urlData = [NSData dataWithContentsOfURL:url];
I added the error: to dataWithContentsofURL
and it returned this:
connectionError NSError * domain: #"NSCocoaErrorDomain" - code: 260 0x166d8af0
I looked at the file in question via Get Info and it starts out like this smb://server/folder/folder/ANC/ANC.pdf

NSData and URLs: There be dragons
+[NSData dataWithContentsOfURL:] can return nil for any number of reasons. If anything goes wrong when attempting to use that URL this method will return nil. For example the file may not exist, the network connection may time out, or the URL may even be malformed. nil is returned and the application has no idea why.
+ [NSData dataWithContentsOfURL:options:error:] on the other hand, will tell the caller what went wrong. When nil is returned the error argument will be populated with an object that describes the problem that occured. Using this method would directly answer the question of "why".
Both of these are synchronous methods and their use for working with files, network resources, and especially files served from a network resource is discouraged. These methods will block the caller and are not really intended for these kinds of uses. It's better to use an input stream or NSURLSession instead.
SMB Not Supported
From your question though it seems you are trying to access a file that exists on an SMB share. Unforunately iOS does not support SMB - even if you had a correctly formatted smb URL (i.e. smb://servername/sharename/filename.pdf) you would be unable to access it without using a third party SMB implementation.
Using FTP in place of SMB, however, should work.

Just need to do some minor change, Actually your pdf file is on server so you have to change your code like this.
NSURL *url = [NSURL URLWithString:stringURL];
I tried this and its working fine.
If your file is in application's main bundle than you can use fileURLWithPath.

If you have a valid windows file path with backslash separators, there is a CoreFoundation method to convert it to a CFURL/NSURL object
NSString *stringURL = #"\\\\SERVER\\FOLDER\\FOLDER\\FTP\\ANC\\ANC.pdf";
NSURL *url = (NSURL *)CFBridgingRelease(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, stringURL, kCFURLWindowsPathStyle, false));
If the string starts with file:// it's a Mac URL string with the file scheme and you can use
NSString *fileURLString = #"file:///Applications";
NSURL *url = [NSURL URLWithString:fileURLString];
Consider that a valid file scheme string must have a third slash for the root folder after the two slashes for the scheme.
The method fileURLWithPath can only be used if the string starts with a single slash, it adds the file:// scheme implicitly.
NSString *pathString = "/Applications";
NSURL *url = [NSURL fileURLWithPath: pathString];

Make sure URL contains http:// OR https://.
For example: http://SERVER/FOLDER/FILE.pdf

Two things to keep in mind while accessing a file in iOS application:
If you are accessing a file from iOS application sandbox(document or cache directory) then use this:
NSURL *url = [NSURL fileURLWithPath:strUrl];
If you are accessing a file from any server in iOS application then use this:
NSURL *url = [NSURL URLWithString:strUrl];
NOTE: You cannot access a file from your mac in iOS application when running from simulator.
Hope this helps!

please check your code with this code
-(void)downloadPdfBtnPressed:(id)sender
{
NSString *dowloadURL = [DownloadUrlArray objectAtIndex:[sender tag ]];
NSString *filename =[dowloadURL lastPathComponent];
NSString *stringPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:#"/Download"];
if (![[NSFileManager defaultManager] fileExistsAtPath:stringPath])
[[NSFileManager defaultManager] createDirectoryAtPath:stringPath withIntermediateDirectories:NO attributes:nil error:nil];
NSData *pdfData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:dowloadURL]];
if(pdfData)
{
stringPath = [stringPath stringByAppendingPathComponent:filename];
[pdfData writeToFile:stringPath atomically:YES];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[singletonObj getTranslationForKey:#"AlereTitleContactus"]
message:#"PDF file is downloaded"
delegate:nil
cancelButtonTitle:[singletonObj getTranslationForKey:#"alertOK"]
otherButtonTitles:nil];
[alert show];
}
}

Related

Objective-C create mapped file for PDF

I am creating a file like so:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,PDFFile];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSFileManager defaultManager] createFileAtPath:filePath contents:dataBytes attributes:nil];
}
_previewItemURL = [NSURL fileURLWithPath:filePath];
and I am displaying it in an UIDocumentInteractionController like so:
if (_previewItemURL) {
UIDocumentInteractionController *documentInteractionController =[UIDocumentInteractionController interactionControllerWithURL:_previewItemURL];
documentInteractionController.delegate = self;
[documentInteractionController presentPreviewAnimated:YES];
}
However, sometimes the PDF file I am saving off bytes are way too big, sometimes 5.5MB, which causes UIDocumentInteractionController to some time to load the PDF. I was doing some reading here https://stackoverflow.com/a/27863508/979331 and it is suggested to create a 'mapped' file. My question is I don't understand how to create one. I have been googling like crazy for the past two days and I just don't understand it.
I think the issue is with the PDF because I tried this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pgnPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.pdf", #"example"]];
//filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,PDFFile];
//if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSString *newFile = [[NSBundle mainBundle] pathForResource:#"example" ofType:#"pdf"];
[[NSFileManager defaultManager] copyItemAtPath:newFile toPath:pgnPath error:&error];
//[[NSFileManager defaultManager] createFileAtPath:filePath contents:dataBytes attributes:nil];
//}
//_previewItemURL = [[NSBundle mainBundle] URLForResource:#"example" withExtension:#"pdf"];
_previewItemURL = [NSURL fileURLWithPath:pgnPath];
with a PDF that was 5.5MB and everything seemed fine, could the issue be with how I getting the PDF? I am getting the bytes from a web service, here is my call:
task = [dataSource.areaData GetPDFFileTestTwo:[NSString stringWithFormat:#"%#",encodedUrlStr] completion:^(NSData *data, NSURLResponse *response, NSError *error) {
NSError *myError;
NSArray *tableArray = [[NSArray alloc]initWithArray:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
NSData *dataBytes;
for (NSDictionary *dict in tableArray) {
NSString *base64 = dict[#"data"];
dataBytes = [[NSData alloc] initWithBase64EncodedString:base64 options:0];
}
if (dataBytes) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,PDFFile];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSFileManager defaultManager] createFileAtPath:filePath contents:dataBytes attributes:nil];
}
_previewItemURL = [NSURL fileURLWithPath:filePath];
if (_previewItemURL) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIDocumentInteractionController *documentInteractionController =[UIDocumentInteractionController interactionControllerWithURL:_previewItemURL];
documentInteractionController.delegate = self;
dispatch_async(dispatch_get_main_queue(), ^{
[documentInteractionController presentPreviewAnimated:YES];
});
});
}
}
}];
And here is GetPDFFileTestTwo
-(NSURLSessionDataTask *)GetPDFFileTestTwo:(NSString *)PDFFile completion:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler{
NSString *FileBrowserRequestString = [NSString stringWithFormat:#"%#?PDFFile=%#",kIP,PDFFile];
NSURL *JSONURL = [NSURL URLWithString:FileBrowserRequestString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
if(completionHandler)
{
completionHandler(data, response, error);
}
}];
[dataTask resume];
return dataTask;
}
kIP is a string and that is the web service URL
You're not "creating" a mapped file. You're reading it into NSData as mapped to the bytes in the file. That means, that in-memory NSData bytes are underneath mapped to bytes in the file.
Here is a way to read a file as mapped:
https://github.com/atomicbird/atomictools/blob/master/NSData%2BreallyMapped.h
If you can't pass NSData to the controller for preview, mapping makes no sense. Even if you can, you have to be sure that controller won't copy your data before it is used.
Consider using PDFKit framework, where you can initialize PDFDocument with NSData and display it in PDFView.
Your questions;
create a 'mapped' file. My question is I don't understand how to
create one.
So let me point out that UIDocumentInteractionController has no inputs that accept NSData, so you wouldn't be able to create a memory mapped file to use with it. I also examined the header for any other clues, and didn't find any.
https://developer.apple.com/documentation/uikit/uidocumentinteractioncontroller
In looking for a solution, I saw that QLPreviewController mentions 'data' but I find it didn't accept NSData either.
https://developer.apple.com/documentation/quicklook/qlpreviewcontroller
I finally settled on WKWebView in WebKit, which does support using NSData, that is Memory Mapped, and loads quite quickly with a 15 MB PDF full of pictures and text I made.
I created a project with the large PDF and tried it out, it works fine. Please feel free to examine it.
https://github.com/eSpecialized/PDFDocViewerMapped
How 'mapped' works;
Anything that can take NSData can use a mapped file unless it needs the entire set of data at once. A PDF with a single image vs a multipage PDF are good examples of what can't be mapped and what can be mapped.
NSError *errors;
NSData *docFileData = [NSData dataWithContentsOfFile:docFileWithPath options:NSDataReadingMappedAlways error:&errors];
Options for NSData mapped;
https://developer.apple.com/documentation/foundation/nsdatareadingoptions?language=objc
NSDataReadingMappedIfSafe // Hint to map the file in if possible and safe
NSDataReadingMappedAlways // Hint to map the file in if possible. This takes precedence over NSDataReadingMappedIfSafe if both are given.
could the issue be with how I getting the PDF?
Fetching a PDF remotely means you must download the document.
Think of what you are doing, fetching the Pdf, saving it locally, then opening it in the UIDocument Interaction controller which takes URL's and not NSData.
I hope this meets your criteria for a solution;
UIDocumentInteractionController limitations with requiring URL's
WKWebView - allows using NSData that can be memory mapped.
3rd party options for PDF viewing - anything that can accept NSData is a
candidate for use. Look for CocoaPods and on GitHub for PDF, iOS PDF,
etc.

How to store .flac file content in NSData?

In my app I have added one .flac file in my resources folder. I want to send to this .flac file to Google's speech recognition service... Below is my code:
NSURL* urlGoogle = [NSURL URLWithString:#"https://www.google.com/speech-api/v1/recognize"];
NSMutableURLRequest *urlGoogleRequest = [[NSMutableURLRequest alloc]initWithURL:urlGoogle];
[urlGoogleRequest setHTTPMethod:#"POST"];
[urlGoogleRequest addValue:#"audio/x-flac; rate=16000" forHTTPHeaderField:#"Content-Type"];
NSURLResponse* response = nil;
NSError* error = nil;
NSArray *docDirs = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [docDirs objectAtIndex:0];
NSString *path = [[docDir stringByAppendingPathComponent:#"surround88"]
stringByAppendingPathExtension:#"flac" ];
NSURL* url = [NSURL fileURLWithPath:path];
//Here I'm getting flacData value is nil
NSData *flacData = [NSData dataWithContentsOfURL:url]; //flacData = nil
[urlGoogleRequest setHTTPBody:flacData];
NSData* googleResponse = [NSURLConnection sendSynchronousRequest:urlGoogleRequest
returningResponse:&response
error:&error];
id jsonObject=[NSJSONSerialization JSONObjectWithData:googleResponse options:kNilOptions error:nil];
NSLog(#"Googles response is: %#",jsonObject);
Since I'm not sending any data to the server, I'm getting empty response.
I have tested other 3rd party apis like openears, dragon, ispeech etc and not satisfied.
Can some one help me how to proceed to further. Is this the correct way to implement google's speech recognition functionality? Any help would be appreciated.
Since you're placing the file in your resources folder, you're not going to find it with NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);. That's why [NSData dataWithContentsOfURL:url] is returning nil.
Your file is now placed in your Bundle, so try loading the file's contents with this:
NSURL *url = [[NSBundle mainBundle] URLForResource:#"surround88" withExtension:#"flac"];
NSData *flacData = [NSData dataWithContentsOfURL:url];
EDIT: based on comments bellow
Make sure the file is a member of the target you're building. In other words:
Select your .flac file
Make sure to check the boxes for the targets you're testing this with
Using the test project above, I was able to successfully create an NSData object from the .flac file.

Getting an image fails to create NSURL

Im trying to get an image from a url link.
NSURL *thmbnailUrl = [NSURL URLWithString:[NSString stringWithFormat:#"http://%#:800/fishtune/movie/%#.png",[[DBHelper sharedInstance] currentHostIP],[arr objectAtIndex:0]]];
NSLog(#"thmbnail:%#",[NSString stringWithFormat:#"http://%#:800/fishtune/movie/%#.png",[[DBHelper sharedInstance] currentHostIP],[arr objectAtIndex:0]]);
NSData *data = [NSData dataWithContentsOfURL:thmbnailUrl];
NSLog(#"movimgdata:%#",data);
UIImage *img = [[UIImage alloc]initWithData:data];
NSLog(#"movimg:%#",data);
NSData *imgData = UIImagePNGRepresentation(img);
when i log thmbnail i get this "ipofserverinnetwork/fishtune/movie/Our Logo 480p back.png"
Note:i didn't put the original ip.
I get null when i log data and of course img will also be null. I really dont know why I'm getting null. because when I tried entering the url in a browser it displays the image. I used the same code in another part of my app and it works but when i used it in this it fails. Is it because of the spaces between "Our_Logo_480p"?
you might need to use
NSString* escapedUrl = [yourstring stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
yes it's because of spaces, use
NSString *myUrl = [NSString stringWithFormat:#"http://%#:800/fishtune/movie/%#.png",[[DBHelper sharedInstance] currentHostIP],[arr objectAtIndex:0]];
NSString *url = [myUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

Save images from URL to iOS Directory

I want to save a lot (800-2000) images from server to iPhone app directory (Documents directory).
First of all I have to check if those image are already on the iPhone directory.
I use this code to download one image:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://a3.twimg.com/profile_images/414797877/05052008321_bigger.jpg"]];
[NSURLConnection connectionWithRequest:request delegate:self];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *localFilePath = [documentsDirectory stringByAppendingPathComponent:#"pkm.jpg"];
NSData *thedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://a3.twimg.com/profile_images/414797877/05052008321_bigger.jpg"]];
[thedata writeToFile:localFilePath atomically:YES];
Please help me, any suggestions:
1. Save all images with the original name on iPhone directory, NOT "pkm.jpg"
2. Save images with names: from 05052008321_bigger to 05052008350_bigger
3. Check if images are downloaded already, DON'T Download again.
I know, maybe this is more than a question. But some suggestions, directions will be really good.
Thanks in advance.
A couple of reactions:
You're initiating a NSURLConnection connectionWithRequest (which triggers the NSURLConnectionDataDelegate methods), but you then apparently disregard that and initiate a dataWithContentsOfURL. You should pick one or the other, but don't do both.
I'd suggest you pursue a NSOperation-based solution because you'll definitely want to (a) enjoy concurrency; but (b) limit the concurrent operations to some reasonable number (say 4 or 5), otherwise you'll have requests timing out and failing.
In terms of getting the filename, you can retrieve the filename using lastPathComponent from the NSURL. (My download operation, used below, actually will automatically use this if you don't provide an explicit filename.)
You haven't illustrated how you're determining the list of filenames from the remote server, so we'd have to see how you know what images there are to retrieve.
If this is for your own purposes, this is fine, but I've heard claims that Apple rejects apps that request too much data over a cellular connection (and 2000 images would certainly qualify). Frankly, even if Apple doesn't raise a fuss, you really should be asking the user before using that much of their data plan. You can use Reachability to determine whether the user is connecting via wifi or via cellular, and if the latter, you may want to present a warning.
But I'd suggest something that looks like (assuming you have some NSArray with NSString versions of the URL's ... obviously adjust this for whatever form your list of URLs is in):
NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
downloadQueue.maxConcurrentOperationCount = 4;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
for (NSString *urlString in urlStrings)
{
NSURL *url = [NSURL URLWithString:urlString];
NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
if (![fileManager fileExistsAtPath:path]) {
DownloadOperation *downloadOperation = [[DownloadOperation alloc] initWithURL:url];
downloadOperation.downloadCompletionBlock = ^(DownloadOperation *operation, BOOL success, NSError *error) {
if (error) NSLog(#"download of %# failed: %#", operation.url, error);
};
[downloadQueue addOperation:downloadOperation];
}
}
And that download operation might be something like this. Obviously, use whatever NSOperation based downloader you want, but I'd suggest you use one that doesn't load the whole download into memory, but rather one that streams it directly to persistent storage.
If you don't want to get that fancy, you could just do something like:
NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
downloadQueue.maxConcurrentOperationCount = 4;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
for (NSString *urlString in urlStrings)
{
NSURL *url = [NSURL URLWithString:urlString];
NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
if (![fileManager fileExistsAtPath:path]) {
[downloadQueue addOperationWithBlock:^{
NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
NSData *data = [NSData dataWithContentsOfURL:url];
if (data)
[data writeToFile:path atomically:YES];
}];
}
}
Clearly, use whatever download operation class you want for downloading, but hopefully you get the basic idea. Create a NSOperation-based download, and then submit one for every file that needs to get downloaded.
I'm not sure the best method to serialize the information (naively, you could just write the NSDictionary to the disk). I would have a large NSDictionary (which could be broken up into smaller ones, up to you how to do that). The NSDictionary would take the image name "05052008321_bigger" and map it to a simple #YES.
When the app is in a position to download a new image, it would read the NSDictionary from disk (can be read in a different thread), and check if the image name is in the dictionary. This allows lookups to be fast.
- (BOOL)checkIfImageHasBeenDownloaded:(NSString *)imageName
{
return [self.dictionaryNames objectForKey:imageName] != nil;
}

Crash when trying to open downloaded pdf in new view

My app downloads a pdf and then on a button press brings it up in a new view.
I get the error:
-[NSURL initFileURLWithPath:]: nil string parameter'
After some troubleshooting I pinned the problem to somewhere in this code snippet. The path that is being pointed to is in the /Documents folder where the downloaded pdf is placed. Thus the document is not in the main bundle.
NSString *path = [[NSBundle mainBundle] pathForResource:PDFpathwithextension ofType:#"pdf"];
NSURL *url = [NSURL fileURLWithPath:path];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
Here's the download code:
//Start an NSURL connection to download from the remotepath
NSData *pdfData = [[NSData alloc] initWithContentsOfURL:remotepathURL];
//Store the Data locally as PDF File
NSString *resourceDocPath = [[NSString alloc] initWithString:[[[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:#"Documents"]];
NSString *filePath = [resourceDocPath stringByAppendingPathComponent:[newdata.ThirdPickerName stringByAppendingFormat:#".pdf"]];
pdfData writeToFile:filePath atomically:YES];
As NSURL is telling you, you've handed it nil instead of a valid path.
nil here means no such resource could be found by that name. Indeed, your question suggests you're well aware of this.
Since you claim your app already downloads a PDF, does it actually write that out to disk? If so, you should know where the resulting file is from doing that. If not, you first need to write the actual download code!

Resources