Swift NSURLSession Download Task not retrieving image assets - html

I am downloading an html page which looks like this from a local Jetty 9 server.
<h1>Hello</h1>
<img src="tiles/tile00.jpg">
<img src="tiles/tile01.jpg">
When I navigate to that page in Chrome, my server receives requests for
/
/tiles/tile00.jpg
/tiles/tile01.jpg
/favicon.ico
and the page displays with the images in Chrome.
However when I navigate to that page using the Swift code below, I only receive a request for
/
and the text renders, but the images only render as [?] icons.
#IBAction func displayWebPage(sender: UIButton) {
let h2Url = NSURL(string: "https://localhost:8443")!
let downloadDelegate = Deleg(view: self)
let ses = NSURLSession(
configuration: NSURLSessionConfiguration.defaultSessionConfiguration(),
delegate: downloadDelegate,
delegateQueue: nil
)
ses.downloadTaskWithURL(myUrl).resume()
}
func completed(location: NSURL) {
print("downloaded")
var htmlString = "NON, MERCÍ"
do {
htmlString = try String.init(
contentsOfURL: location,
encoding: NSUTF8StringEncoding
)
} catch {
print("ERRORED OUT")
fatalError()
}
print("contents: \(htmlString)")
self.webView.loadHTMLString(htmlString, baseURL: nil)
}
class Deleg: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate {
var view: ViewController
init(view: ViewController) {
self.view = view
}
func URLSession(
session: NSURLSession,
downloadTask: NSURLSessionDownloadTask,
didFinishDownloadingToURL location: NSURL
) {
self.view.completed(location)
}
}
Is there a way to convince the NSURLSession to download the assets linked-to in the html page that it downloads?
One solution would be to use let the UIWebView download the linked assets, but then I would not be able to use self-signed certificates.

Related

Is there an easy solution for parsing html in swift to get individual elements into their own variable?

I have code which I found online that pulls the HTML off a website and then prints it out. I need to save these into a variable to I can display/ use these in my app.
I am fairly new to this kind of thing and really just need pointers, I don't mind researching! I just need to know what steps I need to be looking into!
import UIKit
// run asynchronously in a playground
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// create a url
let url = URL(string: "https://www.stackoverflow.com")
// create a data task
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("there's a problem")
}
print(String(data: data!, encoding: String.Encoding.utf8) ?? "")
}
//running the task w/ resume
task.resume()
This (in Xcode playground) takes the HTML and prints it out using:
print(String(data: data!, encoding: String.Encoding.utf8) ?? "")
Can anyone please help me out getting maybe the <title>...</title> element into its own variable?
Parsing HTML without a third party is not achievable without a WebView, BUT YOU CAN easily use a webView and run a getElementsByTagName with JS on it to get anything from the HTML code like this:
1- Define the js code:
let js = "document.getElementsByTagName("title")[0].innerHTML"
2- Import WebKit and load the html into a webView
class MyViewController : UIViewController {
let html = """
<#the HTML code, can be loaded from anywhere#>
"""
override func loadView() {
let webView = WKWebView()
webView.navigationDelegate = self // Here is the Delegate
webView.loadHTMLString(html, baseURL: nil)
self.view = webView
}
}
3- Take the delegation and implement this method:
extension MyViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript(js) {(result, error) in
guard error == nil else {
print(error!)
return
}
print(String(describing: result))
}
}
}
Note 1: remember getElementsByTagName returns an array and you must pass the index you want the get like [0]
Note 2: since it use JavaScriptCore, it can't be done without webView, and it must be run on mainThread. Only safari can do this off main thread, because it has V8 engine.
Note 3: You must wait for delegate to be completed even if you pass the HTML statically
Note 4: you can use a third party framework like SwiftSoap to do this.

Swift HTML content of URL of mobile site

I've got a URL and I'm trying to get the HTML content of the site the following way:
func getHtml(_ urlString: String) -> String? {
guard let url = URL(string: urlString) else {
return nil
}
do {
let html = try String(contentsOf: url, encoding: .ascii)
return html
} catch let error {
print("Error: \(error)")
return nil
}
}
if let html = getHtml("https://m.youtube.com/") {
print(html)
}
My issue is, that this gets me the html of the desktop version of the site, however I need the html of the mobile version.
I'm not looking for a workaround for this specific site, but for a general solution, so that, given any URL of a mobile site, it doesn't default to getting me the html of the desktop site.
If you use Viewcontroller in iOS to get the HTML, you can use hidden WKWebView as an alternative and implement the WKNavigationDelegate which has the didFinish method where you can use webView.evaluateJavaScript. As wkwebview is loading from mobile, you will get the mobile version html. Here is the sample of the code.
import UIKit
import WebKit
class YourViewController: UIViewController, WKNavigationDelegate {
let webView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func BtnClicked(_ sender: Any) {
loadWeb(url: "https://m.youtube.com/")
}
func loadWeb(url: String) {
if let myURL = URL(string: url) {
let request = URLRequest(url: myURL)
webView.navigationDelegate = self
webView.load(request)
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("document.documentElement.outerHTML") { (data, error) in
//use html data
print("data", data, error.debugDescription)
}
}
}

WebView insert local image to html couldn't success?

I have a WkWebView in my ViewController, I used webview load local html. The code is this:
guard let path = Bundle.main.url(forResource: "editor", withExtension: "html") else {
return
}
webView.load(URLRequest(url: path))
1: I choose a image from photoLibrary, then I saved it to document's directory .
private func fileName() -> String {
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd#HH-mm-ss"
return "/" + formatter.string(from: date) + ".png"
}
func saveImage(_ image: UIImage) {
guard let imageData = UIImageJPEGRepresentation(image, 1.0) else {
return
}
let directory = NSHomeDirectory().appending("/Documents")
let path = directory.appending(fileName())
let fileManager = FileManager.default
do {
try fileManager.createDirectory(atPath: directory, withIntermediateDirectories: true, attributes: nil)
fileManager.createFile(atPath: path, contents: imageData, attributes: nil)
} catch {
SHLog("saveImage: \(error)")
}
}
2: I fetched the image path then I used the method of the webView to insert the image.
func evaluateJs(_ imagePath: String) {
webView.evaluateJavaScript("insertImage('\(imagePath)')") { (response, error) in
}
}
insertImage() is a method from the local js file, it can insert the image to html.
If I used the local imagePath, I couldn't insert image to html. But When I used the real url, it can work. I don't know the reason, anyone could help me. Thanks.
Your issue is likely caused by WKWebViews security policies not allowing you access to the local folders of the application.
Check out the documentation for WKWebView loadFileURL. You should be able to pass either the documents directory or full image URL in the allowingReadAccessTo parameter which should let you access the images you want.
If you have a web url of the image, I would prefer to use the web URL anyway rather than trying to store and load the image locally. Your not using up storage in your documents directory and WKWebView is designed to load content from the web.
In my case, assign baseURL your image file's parent folder firstly. Then insert the whole URL path in your html likes following.
<img src="/var/mobile/Containers/Data/Application/C217F3BD-1A1B-438A-A436-D878411C7849/Documents/48C4B007-A950-46D2-A719-E025482710A0/images/79042B1F-E11C-42D1-8B49-DD3A0016F421.jpg"

How to make webview only load specific website like youtube?

I am new to Swift and I'm making an app with webview which can display youtube videos. My problem is I don't want my user to go to another url or something like that. I want to make my app load youtube.com. I'm using HTML instead of a URL because I can tell my webview what height and width it's going to have.
HTML code:
<html><head><style type=\"text/css\">body {background-color: transparent;color: white;}</style></head><body style=\"margin:0\"><iframe frameBorder=\"0\" height=\"" + String(describing: height) + "\" width=\"" + String(describing: width) + "\" src=\"http://www.youtube.com/embed/" + vid.videoId + "?showinfo=0&modestbranding=1&frameborder=0&rel=0\"></iframe></body></html>
You could try using shouldStartLoadWith delegate method of UIWebViewDelegate
extension UIViewController: UIWebViewDelegate {
public func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let url = request.url?.absoluteString ?? ""
print("WebView shouldStartLoadWithRequest url -> \(url)")
if !url.contains("youtube.com") {
// avoid loading strange urls..
// manage error as you need..
webView.stopLoading()
return false
}
return true
}
}
Remember to set UIViewController as UIWebView delegate in viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
myWebView.delegate = self
}

Open specific link in Safari from UIWebView

I have a UIWebView which loads a local index.html file.
However I have an external link in this html file that I'd like to open in Safari instead of internally in the UIWebView.
Opening a link in safari from say a UIButton was simple enough:
UIApplication.sharedApplication().openURL(NSURL(string: "http://www.stackoverflow.com"))
Opening the Instagram app from a external link also works like a charm.
instagram://media?id=434784289393782000_15903882
So my first though was to do something like this:
Open in Safari
However that doesn't seem to work, then I read something about using webView:shouldStartLoadWithRequest:navigationType:
But everyone who's managed to open an external link in Safari is writing in Obj-C which I'm not too familiar with as I'm writing in Swift.
Update with Swift Code:
import UIKit
class AccessoriesViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView:UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
if let url = NSBundle.mainBundle().URLForResource("accessories", withExtension: "html") {
webView.loadRequest(NSURLRequest(URL: url))
}
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent;
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let url = request.URL where navigationType == UIWebViewNavigationType.LinkClicked {
UIApplication.sharedApplication().openURL(url)
return false
}
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is how you can do it!
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let url = request.URL where navigationType == UIWebViewNavigationType.LinkClicked {
UIApplication.sharedApplication().openURL(url)
return false
}
return true
}