Swift / UIWebView with xpath / css - ios

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.

Related

Why am I not able to load valid PDF file with WKWebView?

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.

Get data from Share Extension in Swift

Inside my app I have a CustomShareViewController and with that the workflow should be like this:
User is in Safari on some website and looks up any kind of product
User taps on "share" and selects my App
I parse data from the current URL and use it/store it inside my app
Problem:
How do I get the data in Swift? I managed to get the current URL and with that the HTML like this:
#objc func actionButtonTapped(){
var html: String?
if let item = extensionContext?.inputItems.first as? NSExtensionItem,
let itemProvider = item.attachments?.first,
itemProvider.hasItemConformingToTypeIdentifier("public.url") {
itemProvider.loadItem(forTypeIdentifier: "public.url", options: nil) { (url, error) in
if (url as? URL) != nil {
html = (self.getHTMLfromURL(url: url as? URL))
self.doStuff(html: html)
}
}
}
}
But is there any way to get the data that Apple provides? (image and title) ?
That's the data from Apple I mean:
With #Filip's hint I found this Git-Repo:
URLEmbeddedView
With this I can simply get the data like this:
let urlString = ...
OpenGraphDataDownloader.shared.fetchOGData(urlString: urlString) { result in
switch result {
case let .success(data, isExpired):
// do something
case let .failure(error, isExpired):
// do something
}
}

Swift Grand Central Dispatch Queues and UIImages

I know this type of question has been asked 1e7 times but I have come across a specific issue that I don't think has been covered/is blatantly obvious but I am too novice to fix it on my own.
I have the following code snippet within my cellForRowAt method in a TableViewController:
let currentDictionary = parser.parsedData[indexPath.row] as Dictionary<String,String>
let urlString = currentDictionary["media:content"]
if urlString != nil {
let url = NSURL(string: urlString!)
DispatchQueue.global().async {
let data = try? Data(contentsOf: url! as URL) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
DispatchQueue.main.async {
cell.thumbnailImageView.image = UIImage(data: data!)
}
}
}
Which executes fine, downloads the images and assigns them to the UIImageView of each tableViewCell.
There is a finite delay when scrolling the table as the images are downloaded 'on the fly' so to speak.
What I want to do is pre-download all these images and save them in a data structure so they are fetched from URL's less frequently.
I have tried the following implementation:
var thumbnail = UIImage()
for item in parser.parsedData {
let currentDictionary = item as Dictionary<String,String>
let title = currentDictionary["title"]
let link = currentDictionary["link"]
let urlString = currentDictionary["media:content"]
let url = NSURL(string: urlString!)
if urlString != nil {
let url = NSURL(string: urlString!)
DispatchQueue.global().async {
let data = try? Data(contentsOf: url! as URL)
DispatchQueue.main.sync {
thumbnail = UIImage(data: data!)!
}
}
}
var newsArticle: News!
newsArticle = News(title: title!, link: link!, thumbnail: thumbnail)
news.append(newsArticle)
Where news is my data structure. This code also executes fine, however each thumbnail is a 0x0 sized image, size {0, 0} orientation 0 scale 1.000000, according to the console output.
Does anyone have any ideas how to download these images but not immediately assign them to a UIImageView, rather store them for later use?
The problem is that you create your newsArticle before the global dispatch queue even started to process your url. Therefore, thumbnail is still the empty UIImage() created in the very first line.
You'll have to create the thumbnail inside the inner dispatch closure, like:
for item in parser.parsedData {
guard let currentDictionary = item as? Dictionary<String,String> else { continue /* or some error handling */ }
guard let title = currentDictionary["title"] else { continue /* or some error handling */ }
guard let link = currentDictionary["link"] else { continue /* or some error handling */ }
guard let urlString = currentDictionary["media:content"] else { continue /* or some error handling */ }
guard let url = URL(string: urlString) else { continue /* or some error handling */ }
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.sync {
if let thumbnail = UIImage(data: data) {
let newsArticle = News(title: title, link: link, thumbnail: thumbnail)
news.append(newsArticle)
}
}
}
}
}
By the way, your very first code (cellForRow...) is also broken: You must not reference the cell inside the dispatch closure:
DispatchQueue.main.async {
// Never do this
cell.thumbnailImageView.image = UIImage(data: data!)
}
Instead, reference the IndexPath, retrieve the cell inside the clousure, and go on with that cell. But as you already mentioned, there are many many entries on stackoverflow regarding this issue.

let url = URL(string: item)! returning nil in swift

Really cannot figure this one out, the URL prints and is not equal to nil, and it works in the browser when I paste it in. Any ideas?
import UIKit
class WebViewController: UIViewController {
var postLink: String = String()
#IBOutlet weak var mywebView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
print(postLink)
let attempt = postLink
let url: URL = URL(string: attempt)!
let request: URLRequest = URLRequest(url: url)
mywebView.loadRequest(request)
}
The error occurs at:
let url: URL = URL(string: attempt)!
I am guess you are passing the urlString from another controller, do that instead
var postUrlString:String? //<-- make it optional
override func viewDidLoad() {
super.viewDidLoad()
guard let urlString = postUrlString, // forced unwrapped
let url = URL(string: urlString)
else { return } // if there is any optional we return
// else continue
let request: URLRequest = URLRequest(url: url)
mywebView.loadRequest(request)
}
The error is simple, postLink, you are providing to create URL is not correct. My guess is its empty.(Just a guess) and you have forgot to set it.
Avoid using force unwrapping ! in your code as much as possible.
You should either use guard let or if let in the scenarios.
In your case you might want to show some error to user when you are unable to load. Instead of
let url: URL = URL(string: attempt)!
use
if let url = URL(string: attempt) {
let request = URLRequest(url: url)
mywebView.loadRequest(request)
} else {
// Do something like. Show an alert that could not load webpage etc.
}
Alternatively you can use guard let, but it would require to return from the function where it is used. To know more about uses of if and guard let you can go through by blog post here.

"Unexpectedly found nil" but the value is successfully checked with "print". Swift

Getting an "unexpected found nil" error, but when checking the value - its there:
override func viewDidLoad() {
super.viewDidLoad()
if whichLink == "official link" {
let urlStr = videoGame.offLink!
let url = NSURL(string: urlStr)!
let request = NSURLRequest(URL: url)
webView.loadRequest(request)
}
else if whichLink == "moby game link" {
print("yo yo yo, value is here! \(videoGame.mgLink) ")
let urlStr1 = videoGame.mgLink!
let url1 = NSURL(string: urlStr1)!
let request = NSURLRequest(URL: url1)
webView.loadRequest(request)
}
}
I'm suspecting an error in storyboard... but can't locate anything.
Did anyone has a clue what can be wrong?
The full project can be found # https://github.com/flostik2008/Favorite-Games
Your URL string is incorrectly formatted with the space at the end, so the NSURL initialization is returning nil.
You should URL encode all raw strings before trying to create an NSURL:
let urlStr1 = videoGame.mgLink!.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())! should work

Resources