Open PDF link to PDF Reader in iOS - ios

Am getting the PDF file link from my web view. when i click that link, i want to open that PDF file in my iBook application.
i got the contents from server. I showed that content in UIWebview like this.
In this content have a PDF link "Emerging Trends in Real Estate". When i choose this link, i want to open this pdf file in iPhone PDF reader applications like iBook, Adobe Reader. When i click this link it goes to webView Delegate method
-(BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL* url = [request URL];
NSLog(#"PDF URL : %#",url);
if (UIWebViewNavigationTypeLinkClicked == navigationType)
{
if ([url isEqual:#"about:blank"])
{
return YES;
}
else
{
// Initialize Document Interaction Controller
documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:url];
// Configure Document Interaction Controller
documentInteractionController.delegate = self;
// Present Open In Menu
[documentInteractionController presentOptionsMenuFromRect:CGRectZero inView:self.view animated:YES];
}
}
return YES;
}
when compiler comes to else part i got an error. i displayed that error :
PDF URL : http://www.uli.org/wp-content/uploads/ULI-Documents/Emerging-Trends-in-Real-Estate-Americas-2014.pdf
2014-01-17 16:44:49.233 ULINewYork[3163:a0b] *** Assertion failure in -[UIDocumentInteractionController setURL:], /SourceCache/UIKit_Sim/UIKit-2903.23/UIDocumentInteractionController.m:1010
2014-01-17 16:44:49.234 ULINewYork[3163:a0b] *** WebKit discarded an uncaught exception in the webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: delegate: <NSInternalInconsistencyException> UIDocumentInteractionController: invalid scheme http. Only the file scheme is supported.
please give me some idea to handle this process.

I tried the same way you have used along with the help of apple documentation. I get the same error you mentioned, then I download the DocInteraction application for understanding the UIDocumentInteractionController In that application they use this class to open the files located in local app sandbox or the files located in the main bundle.
If your Intention is to make the user read the pdf file then leave the handling of request to the webview itself(take out the -webView: shouldStartLoadWithRequest: delegate method)
or else if you want to show some other options the ios device could do(print,preview,mail etc) with the file then you have to download that pdf file to local then set the url property of UIDocumentInteractionController object to the local path url you have saved the file before presenting.

Related

How to open a Link to a PDF with wkwebview

I created a simple iOS app, which opens a URL with WKWebView. On the website, there is a link to a PDF Document. When I open the site on my browser, I can click onto the Link, and the PDF document opens. But on my App, nothing happens when I click onto the link.
How can I fix it? Do I have to put something into my info.plist?
SWIFT 3.* & 4.* *
First you have to download that pdf file into your app, after downloading you have to get that file path, then that file path should be use like following way in WKWebView.
let fileURL = URL(fileURLWithPath: filePathURLData as! String)
//print(fileURL)
webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
Here filePathURLData is your actual file path which you have downloaded into your app, you have to convert this into URL, then you need to load that file into WKWebView
Thanks
Hope this will help you.
This will show any file in the WKWebView (doc, docx, xlsx, pdf, google doc, pages & Any textfile)
Likely, you are using target="_blank" in your anchor tag. That opens up a new window to display the link. WKWebView is blocking your attempt to open a new window (at least by default).
The code below still does not create a new window, but instead opens the PDF, etc link in the current WKWebView.
The other option seems to be to create a new WKWebView and return it, so the ios and open the link in that, but I don't want extra Views being created by every click on a website inside the WKWebView.
In your ViewController.viewDidLoad
webView.uiDelegate = self
Then add the extension for the delegate
extension ViewController: WKUIDelegate {
/**
* Force all popup windows to remain in the current WKWebView.
* By default, WKWebView is blocking new windows from being created
* ex text.
* This code catches those popup windows and displays them in the current WKWebView.
*/
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
// open in current view
webView.load(navigationAction.request)
// don't return a new view to build a popup into (the default behavior).
return nil;
}
}
I have same issue, and found out the pdf file is using unsecured http. Therefore the app refuse to open it.
Try to check with https link and see if it works.
Also with WKWebView, you don't need to download the pdf file before open it. Just load the url directly, i.e
webView.load(URLRequest(url: URL(string: "https-pdf-link")!))
Objective C:
NSURL *fileUrl = [[NSURL alloc] initWithString:filePathURLData];
[webView loadFileURL:fileUrl allowingReadAccessToURL:fileUrl];
Another way to open PDF saved in document directory:
NSData *data = [NSData dataWithContentsOfFile:fileURL];
[webView loadData:data MIMEType:#"application/pdf" characterEncodingName:#"" baseURL:[NSURL URLWithString:fileURL]];

How to implement Open In apps for plain text

I would like to provide the ability for users to tap the Action button and up pops the usual share sheet, which should include other apps to the right of the Messages, Facebook, etc icons - applications that can work with .txt files, or just an NSString.
I am currently displaying a Share sheet via UIActivityViewController, which is working great but it does not include other apps in the list. From reading other SO questions I concluded it's only possible to get those other apps to appear if you use UIDocumentInteractionController instead. I looked into creating a .txt file in a temp directory to share that file (instead of just sharing an NSString), but only Mail (no Copy) shows up when I tap the Share button. [Do note that if I run it on a real device not the simulator more apps other than Mail will appear and AirDrop too.] When I tap Mail, the app crashes: Unable to get data for URL: The operation couldn’t be completed. (Cocoa error 260.) Something is wrong with the way I'm creating/retrieving the .txt file.
My questions are:
Why is my code resulting in a crash when attempting to share the .txt file?
How can I get the Copy option to appear in the same Share sheet as the one that includes other apps?
To summarize: I need a share sheet that includes: Copy, AirDrop, Messages, Mail, Facebook, Twitter, Pages, Dropbox, etc for a simple string of text. Thanks!
The following lines of code lie inside my IBAction share button tap function:
UIActivityViewController approach:
UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:#[self.myUITextField.text] applicationActivities:nil];
[self presentViewController:activityView animated:YES completion:nil];
Result:
UIDocumentInteractionController approach:
NSString *fileName = [NSString stringWithFormat:#"%#mytextfile.txt", NSTemporaryDirectory()];
[self.myUITextField.text writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
NSURL *textFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:#"mytextfile.txt"]];
UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:textFileURL];
[documentInteractionController presentOptionsMenuFromBarButtonItem:sender animated:YES];
Result (will show more apps and AirDrop if I run on a real device):
Example of what I want to obtain - minus the 3 extra options at the bottom:
If I cannot obtain the above screenshot with a string (instead of a photo) for some reason, I am willing to implement it how Dropbox has done it. They added an Open In button at the bottom that presents a different sheet that only shows additional apps. Note that I would still need a Copy option on the original sheet.
Question 1: Why is my code resulting in a crash
Cocoa error 260 is an NSFileReadNoSuchFileError according to the Foundation Constants Reference document. Looking at your code, the only way how I can see that file creation might fail is if self.myUITextField is nil.
I suggest that you check this first. If the property is not nil, then check whether writeToFile:atomically:encoding:error: returns an error.
Question 2: How can I get the Copy option to appear
First, assign a delegate to the controller:
documentInteractionController.delegate = self;
Then implement the following two delegate methods:
- (BOOL) documentInteractionController:(UIDocumentInteractionController*)controller canPerformAction:(SEL)action
{
if (#selector(copy:) == action)
return YES;
else
return NO;
}
- (BOOL) documentInteractionController:(UIDocumentInteractionController*)controller performAction:(SEL)action
{
if (#selector(copy:) != action)
return NO;
// Perform the copy: action
return YES;
}
Both methods are marked deprecated since iOS 6, but they still seem to work in iOS 7. Unfortunately, I have no idea how to implement the copy: action without those two methods - and neither does Apple, or so it seems to me, since they do not offer a replacement, and the official Document Interaction Programming Topics for iOS document still happily refers to the methods without indication that they are deprecated.
Anyway, here's a simple but complete implementation of the second delegate method:
- (BOOL) documentInteractionController:(UIDocumentInteractionController*)controller performAction:(SEL)action
{
if (#selector(copy:) != action)
return NO;
NSStringEncoding usedEncoding;
NSError* error;
NSString* fileContent = [NSString stringWithContentsOfURL:controller.URL
usedEncoding:&usedEncoding
error:&error];
UIPasteboard* pasteboard = [UIPasteboard generalPasteboard];
[pasteboard setString:fileContent];
return YES;
}

iOS - Check if webView is loaded with PDF content on non-'pdf' file extension

I would like to Check (boolean value) if PDF content is loaded in the UIWebView. This question already exists, but not this case.
It's not as simple as just checking the path extension of the webView Url because this PDF content is generated via ASPX. The extension will return 'aspx'. Also not just looking in the url, the string 'pdf' will always be found in the url since I requesting a PDF file. I need to check if the server really returns a PDF file as it should.
Ok, The webView is loaded with PDF content but not to a specific pdf file on the server. (maybe apache redirect,aspx pdf generation or just something else)
If this url is loaded in the Safari Application (to the aspx file), it will detect this PDF content (even if aspx file). It says "open in..." and so on.
How to detect if the UIWebView is loaded with PDF content on non-pdf extention file?
Jonathan
I solved it using this:
Thanks #Guilherme
-(bool)isPDFContentLoadedInWebView:(UIWebView *)webView {
NSString *mime =[[NSURLCache sharedURLCache] cachedResponseForRequest:webView.request].response.MIMEType;
if (!([mime rangeOfString:#"pdf"].location == 0)) {
return YES;
} else {
return NO;
}
return NO;
}

Can I send UIDocuments by email, as an attachment?

I'm working with Xcode.
In my app I save some UIdocuments at that location
[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
I'm searching for a way to share documents, my first option is by email.
Can I send those documents by email, as an attachment? Can I open then with another device with the same app?
You could do like the following.
Create a MFMailComposeViewController and use - (void)addAttachmentData:(NSData*)attachment mimeType:(NSString*)mimeType fileName:(NSString*)filename method to add your attachment.
For example.
MFMailComposeViewController *mailVC = [[MFMailComposeViewController alloc] init];
[mailVC setSubject:#"Shared documents"];
[mailVC setToRecipients:#[#"sample#example.com"]];
[mailVC setMessageBody:#"Here the docs I want to share" isHTML:NO];
[mailComposer addAttachmentData:pdfData mimeType:#"application/pdf" fileName:#"file.pdf"];
[mailVC setMailComposeDelegate:self];
[self presentViewController:mailVC animated:YES completion:nil];
where pdfData is of type NSData. So, you need to transform your document into a NSData.
From Apple doc.
addAttachmentData:mimeType:fileName:
This method attaches the specified data after the message body but
before the user’s signature. You may attach multiple files (using
different file names) but must do so prior to displaying the mail
composition interface. Do not call this method after presenting the
interface to the user.
About the second part of your question. Could you explain what type of document do you need to display?
In the meantime, take a look at Adding "Open In..." option to iOS app.
To send any attachment you need to get the contents into an NSData object. If the document is on disk then this is simple. You just need the path or file URL to the document. Then you can create the NSData object using the path or URL.
If the receiver of the email has the same app and the app is setup to appear in the "Open In" menu for documents of this type, then the user can open the app from the attachment. Your app then just needs to know what to do when it is asked to open a file of this type. There are plenty of existing documentation and questions here on SO that describe how to register an app to open certain file types.

UIDocumentInteractionController doesn't open the app (didEndSendingToApplication: never called)

I have a UIDocumentInteractionController instance (that DOES have a strong reference in my class, I am aware of the memory issues about it) and I want to send a photo file to Instagram.
I saved the file using the ig extension (tried igo as well) and I am presenting the controller. Instagram is displayed on the list. I tap Instagram, and nothing happens.
NSURL *imageFile = [NSURL fileURLWithPath:path];
interactionController = [UIDocumentInteractionController interactionControllerWithURL:imageFile];
interactionController.UTI = #"com.instagram.photo";
interactionController.annotation = [NSDictionary dictionaryWithObject:#"my caption" forKey:#"InstagramCaption"];
interactionController.delegate = self;
[interactionController presentOpenInMenuFromRect:self.view.frame inView:self.view animated:YES];
To investigate further, I've set my calling class as a delegate and implemented the willBeginSendingToApplication: and didEndSendingToApplication: methods. Interestingly, I've realized that willBeginSendingToApplication: does get called, but didEndSendingToApplication: does not. I've tried changing my file extensions, changing UTI to com.instagram.exclusivegram, checking if the file URL is correct etc. but none of them seem to work. No error, nothing in the console or anything. The interaction controller closes, my app keeps working as it was working before, just nothing happens. I've read that there can be some issues on iOS 6, but my app is an iOS 6 app, so I can't test it on iOS < 6. The only thing that is close to my problem that I've found is UIDocumentInteractionController, No File Extension but UTI but it dives too much into the low level bits, nor I have a non-ARC code.
What could be the cause of the problem?
This can happen if the file doesn't exist, but also if you haven't constructed the file URL correctly. This plagued me for a while.
Make sure you construct your file URL like this:
NSURL *pagesURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"MyGreatPDF.pdf" ofType:nil]];
and not like this:
NSURL *pagesURL = [NSURL fileURLWithPath:#"MyGreatPDF.pdf"];
The latter still forms a valid URL, but it gets a "private" prefix, i.e. file:///private/var/mobile/Applications/00000000-0000-0000-0000-000000000000/MyGreatApp.app/MyGreatPDF.pdf rather than file:///var/mobile/Applications/00000000-0000-0000-0000-000000000000/MyGreatApp.app/MyGreatPDF.pdf
After a long while, I've found out that the file was not saved correctly, and didn't exist. iOS wasn't throwing out any sort of an error, failing silently. I've corrected the code about generating the file, and when the file was there, the controller appeared. Maybe Apple should add some assertion/exception mechanism for handling non-existent files in document interaction controller.
This may also caused by the file name's extension.
If the target app declare it only support file with png extension in Info.plist -> Exported Type UTIs -> Equivalent Types -> public.filename-extension, and you send a file with jpg extension, the target app won't open as well.

Resources