I am trying to load PDF from URL using WKWebiew using following code and I have also added necessary delegate methods of WKWebiew.
func loadPDFDocument()
{
if let url = URL(string: self.contentURL)
{
print("URL: \(url)")
if UIApplication.shared.canOpenURL(url) {
self.webView.navigationDelegate = self
self.webView.load(URLRequest(url: url))
} else {
self.showInvalidURLError()
}
} else {
self.showInvalidURLError()
}
}
It is loading but actual content is not showing up, instead it shows like following image:
Now, I have tried it with PDFKit using following code and it is loading the actual content.
func loadPDFDocument()
{
let pdfView = self.createPdfView(withFrame: self.view.bounds)
if let pdfDocument = self.createPdfDocument() {
self.view.addSubview(pdfView)
pdfView.document = pdfDocument
}
}
func createPdfDocument() -> PDFDocument?
{
if let resourceUrl = URL(string: "https://d1shcqlf263trc.cloudfront.net/Engage/Contents/LearningStore/16335111672611633500529010123TestPDFfile06Oct2021.pdf") {
return PDFDocument(url: resourceUrl)
}
return nil
}
func createPdfView(withFrame frame: CGRect) -> PDFView
{
let pdfView = PDFView(frame: frame)
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
pdfView.autoScales = true
return pdfView
}
The reasons why I want to load this PDF using WKWebiew are following:
I can get callback once URL did finish loading (with or without error)
I also need to load other types of content e.g. PPT, so I can reuse WKWebiew code.
What may be the issue due to which that PDF is not being able to load using WKWebiew. Is that issue with PDF, or with URL or the way I load it with WKWebiew?
The code you have provided directly does not work, and leads to the following error
WebPageProxy::didFailProvisionalLoadForFrame: frameID=3, domain=WebKitErrorDomain, code=102
I did not find any official apple documentation for this specific error but a simple search points to some kind of interruption.
As an alternative, you can try to load data from the URL:
if let data = try? Data(contentsOf: url) {
self.webView.load(data, mimeType: "", characterEncodingName: "", baseURL: url)
}
But this leads to the weird page you have shown in your question.
Here, the web view does not know that you are trying to show a PDF. Simply providing the content type fixes the issue.
if let data = try? Data(contentsOf: url) {
self.webView.load(data, mimeType: "application/pdf", characterEncodingName: "UTF8", baseURL: url)
}
So, you need to make sure the web view knows what type of content you're going to load.
How you would do that for various file types you want to support is a different question you need to figure out.
Related
I am making PDF using TPPDF library. This makes the PDF fine and returns a URL which is like this:
pdfURL = file:///Users/taimoorarif/Library/Developer/CoreSimulator/Devices/8A2723A7-DD69-4551-A297-D30033734181/data/Containers/Data/Application/EE60FB55-13AE-4658-A829-8A85B2B6ED95/tmp/SwiftUI.pdf
If I open this URL in Google Chrome, this shows my PDF. But when I try to use
UIApplication.shared.open(pdfURL)
It did nothing.
I also made a UIViewRepresentable PDFKitView:
import SwiftUI
import PDFKit
struct PDFKitView: View {
var url: URL
var body: some View {
PDFKitRepresentedView(url)
}
}
struct PDFKitRepresentedView: UIViewRepresentable {
let url: URL
init(_ url: URL) {
self.url = url
}
func makeUIView(context: UIViewRepresentableContext<PDFKitRepresentedView>) -> PDFKitRepresentedView.UIViewType {
let pdfView = PDFView()
pdfView.document = PDFDocument(url: self.url)
pdfView.autoScales = true
return pdfView
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PDFKitRepresentedView>) {
// Update the view.
}
}
And use it as:
PDFKitView(url: URL(string: pdfURL.absoluteString))
It also not worked.
I know this url is the path where this file is saved. So, after searching on this, I tried:
let fileURL = Bundle.main.url(forResource: pdfURL, withExtension: "pdf")!
And application is crashing here with error:
Unexpectedly found nil while unwrapping an Optional value
The question here is I want to open this PDF in my APP, whether it opens on Safari or Google Drive but don't know how to do that. So how can I open my PDF?
Bundle.main is not FileManager. Bundle.main is for accessing your app's resources like a video, image that you added to your project using Xcode.
URL(string:) is meant for online urls. To initialize an URL for a file use URL(fileURLWithPath: anyURL.path). So what you really should do is:
PDFKitView(url: URL(fileURLWithPath: pdfURL.path))
or
PDFKitView(url: pdfURL)
I have been using UIWebView to display Microsoft Office documents (Word, PowerPoint, Excel) in my application for a while but Apple has recently deprecated the UIWebView class. I am trying to switch to WKWebView but Word, Excel, and Powerpoint documents are not rendering properly in WKWebView.
Using UIWebView to display an Excel document (worked great):
let data: Data
//data is assigned bytes of Excel file
let webView = UIWebView()
webView.load(data, mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", textEncodingName: "UTF-8", baseURL: Bundle.main.bundleURL)
Attempting to use WKWebView to do the same thing (displays a bunch of nonsense characters instead of the Excel file):
let data: Data
//data is assigned bytes of Excel file
let webView = WKWebView.init()
webView.load(data, mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", characterEncodingName: "UTF-8", baseURL: Bundle.main.bundleURL)
Due to the nature of my use case, I cannot save the data to disk for security reasons so I cannot use methods like this:
webView.loadFileURL(<#T##URL: URL##URL#>, allowingReadAccessTo: <#T##URL#>)
I also cannot use QuickLook (QLPreviewController) because it again requires a URL.
---------------------------------------------------------------EDIT---------------------------------------------------------
I am also aware of this method of passing the data in via a string URL but unless someone can prove that the data is never written to disk, I cannot accept it as an answer:
let data: Data
//data is assigned bytes of Excel file
let webView = WKWebView.init()
let urlStr = "data:\(fileTypeInfo.mimeType);base64," + data.base64EncodedString()
let url = URL(string: urlStr)!
let request = URLRequest(url: url)
webView.load(request)
This feels like a bug in WKWebView.load(_ data: Data, mimeType MIMEType: String, characterEncodingName: String, baseURL: URL) -> WKNavigation?. It should work in the way we were trying to use it but here is how we got around the issue:
Declare your WKWebView and a custom scheme name
let webView: WKWebView
let customSchemeName = "custom-scheme-name"
Create a subclass of WKURLSchemeHandler. We are using the webView to display a single document (PDF, Word, PowerPoint, or Excel) for the life of the webView so we pass in that document as Data and the FileTypeInfo which is a custom class we made that has the file's MIME type among other things, in the init of the WKURLSchemeHandler.
private class ExampleWKURLSchemeHandler: NSObject, WKURLSchemeHandler {
private let data: Data
private let fileTypeInfo: FileTypeInfo
init(data: Data, fileTypeInfo: FileTypeInfo) {
self.data = data
self.fileTypeInfo = fileTypeInfo
super.init()
}
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
if let url = urlSchemeTask.request.url, let scheme = url.scheme, scheme == customSchemeName {
let response = URLResponse.init(url: url, mimeType: fileTypeInfo.mimeType, expectedContentLength: data.count, textEncodingName: nil)
urlSchemeTask.didReceive(response)
urlSchemeTask.didReceive(data)
urlSchemeTask.didFinish()
}
}
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
//any teardown code you may need
}
}
Instantiate your webView with the custom scheme handler class you just made:
let webViewConfiguration = WKWebViewConfiguration()
let webViewSchemeHandler = ExampleWKURLSchemeHandler.init(data: data, fileTypeInfo: fileTypeInfo)
webViewConfiguration.setURLSchemeHandler(webViewSchemeHandler, forURLScheme: customSchemeName)
self.webView = WKWebView.init(frame: .zero, configuration: webViewConfiguration)
Tell the webView to load the document using a URL that matches your custom scheme. You can pass whatever you want in the url after the customSchemeName prefix but for our use case we didn't need to because we already passed the document that we wanted to display in the initializer of the WKSchemeHandler:
guard let url = URL.init(string: "\(customSchemeName):/123") else {
fatalError()
}
webView.load(URLRequest.init(url: url))
I'm trying to open a .pdf file after download which is downloaded with Alamofire. But I've seen only using a "webview". Thus the application consumes lots of memory and is not viable.
What I want is to open it with the native device application. Any suggestions? Thank you.
Edit: This is my code for download file:
var localPath: NSURL?
Alamofire.download(.GET, url, destination: { (temporaryURL, response) in
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename
localPath = directoryURL.URLByAppendingPathComponent(pathComponent!)
return localPath!
})
.response { (request, response, _, error) in
if error != nil
{
// got an error in getting the data, need to handle it
print("Error: \(error!)")
}
//print(response)
print("Download file en:\(localPath!)")
self.view.hideToastActivity()
//self.actioncall()
}
}
I need open file from localpath...
You should use UIDocumentInteractionController. You can read about it on this Apple documentation page.
By doing some Googling you should see even some example implementations. For example here you can see some code about this done by "mattneub".
I let you one more code that you can use:
var documentInteractionController: UIDocumentInteractionController!
#IBAction func openDocument(sender: UIButton) {
let URL: NSURL = NSBundle.mainBundle().URLForResource("yourPDF", withExtension: "pdf")!
if (URL != "") {
// Initialize Document Interaction Controller
self.documentInteractionController = UIDocumentInteractionController(URL: URL)
// Configure Document Interaction Controller
self.documentInteractionController.delegate = self
// Present Open In Menu
self.documentInteractionController.presentOptionsMenuFromRect(sender.frame, inView: self.view, animated: true)
//presentOpenInMenuFromRect
}
}
// UIDocumentInteractionControllerDelegate
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController {
return self
}
I have an element of a website that I want to display within a UIWebView. The element has a unique ID for css as well as for xpath. Is it possible? How would I be able to do that? Help is very appreciated.
I usualy use this code:
let url : NSURL! = NSURL(string: "http://blablablabla.com")
webView.loadRequest(NSURLRequest(URL: url))
But I don't want to display the whole page. Only the element with that ID.
I assume you are using UIWebView, not the newer WKWebView. What you need is an HTML parser (I chose HTMLReader). After downloading the page content, extract the div you want and replace the page's body with the innerHTML of that div.
The code below gets the Did you know section on Wikipedia:
import HTMLReader
override func viewDidLoad() {
super.viewDidLoad()
loadHTML()
}
func loadHTML() {
let url = NSURL(string: "https://en.wikipedia.org/wiki/Main_Page")!
let request = NSURLRequest(URL: url)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request) { data, response, error in
guard error == nil else {
print(error!.localizedDescription)
return
}
guard let data = data else {
print("data is nil")
return
}
let html = HTMLDocument(data: data, contentTypeHeader: nil)
if let head = html.firstNodeMatchingSelector("head"),
didYouKnow = html.firstNodeMatchingSelector("#mp-dyk") {
let newHTML = "<html><head>\(head.innerHTML)</head><body>\(didYouKnow.innerHTML)</body></html>"
self.webView.loadHTMLString(newHTML, baseURL: url)
}
}
task.resume()
}
This is rather basic and is not 100% fool-proof though. See if it solves your problem.
Thanks for the help in my last question. This time I would like to ask for help again for an application whose contents need to be downloaded and cached when it's opened for the first time.
Indeed it's a web app where the view controller consists of a WebView. In order to cache the whole website (which consists of "index.htm", "first.htm, "second.htm" and etc), I have scraped the whole site using the Kanna library and hence generated numerous links (generatedURL). Then I write the HTML of each link into a single file using the approach answered here. Read and write data from text file
Here is my code in the didFinishLaunchingWithOptions of AppDelegate.swift.
// get the documents folder url
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
for index in 0..<generatedURL.count {
let fileDestinationUrl = documentDirectoryURL.URLByAppendingPathComponent(String(index)+".htm")
cachedURL[index] = fileDestinationUrl //store the cached urls
let fileURL = NSURL(string: generatedURL[index])
//if (NSFileManager.defaultManager().fileExistsAtPath(fileDestinationUrl)) {
let data = NSData(contentsOfURL: fileURL!)
if (data != nil) {
//writing to disk
data?.writeToURL(fileDestinationUrl, atomically: true)
// saving was successful. any code posterior code goes here
//reading from disk
do {
let mytext = try String(contentsOfURL: fileDestinationUrl, encoding: NSUTF8StringEncoding)
print(fileDestinationUrl)
print(mytext) // "some text\n"
} catch let error as NSError {
print("error loading from url \(fileDestinationUrl)")
print(error.localizedDescription)
}
}
// } else {
// print("The files already exist")
// //reading from disk
// do {
// let mytext = try String(contentsOfURL: fileDestinationUrl, encoding: NSUTF8StringEncoding)
// //print(fileDestinationUrl)
// //print(mytext) // "some text\n"
// } catch let error as NSError {
// print("error loading from url \(fileDestinationUrl)")
// print(error.localizedDescription)
// }
//
// }
}
When running the program, the HTMLs of all the links are stored locally in those files. There's no problems in loading the HTML and thereby displaying the cached page in the WebView.
file:///var/mobile/Containers/Data/Application/.../Documents/0.htm
file:///var/mobile/Containers/Data/Application/.../Documents/1.htm
file:///var/mobile/Containers/Data/Application/.../Documents/2.htm
.
.
.
However, the current problem is that I lost the linkage between the cached pages. For example, in the website, there is a button on "index.htm" that links to "first.htm".
Now after loading the cached "index.htm" which is now "file:///var/....../0.htm", I won't be able to go to the cached "first.htm" because "file:///var/....../1.htm" is not in the HTML of the button.
So how do I retrieve the cached files in their original urls? Should I change the approach of generating the file or just create a new version of the website with all the cached file paths?
Thanks for reading my question.
OK, i think I can answer my own question now. Using the following function in the ViewController.swift containing the webView object, I can prompt the webView to load the cached url if the original url is clicked.
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == UIWebViewNavigationType.LinkClicked {
if (request.URL!.absoluteString == generatedURL[index] {
let requestObj = NSMutableURLRequest(URL: appDelegate.cachedURL[index]!);
webView.loadRequest(requestObj)
//return false
}
}
return true
}