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
}
Related
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.
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)
}
}
}
I have a UIViewController with UIWebViewDelegate. I found that I can present it just fine but not push it. If I try to push it the target view controller loads and executes any variables set but does not fire viewDidLoad() and instead just returns to my calling menu view controller without error.
I set a breakpoint on the first var declaration after the class and stepped through the code. When it got to webView init it flashed up the home (not target) view controller in the simulator and then continued though the var declarations before returning back to my home view controller.
Here is my menu code
works:
let myWebViewController = MyWebViewController()
myWebViewController.urlString = "myUrl"
present(myWebViewController, animated: true, completion: nil)
does not work
let myWebViewController = MyWebViewController()
myWebViewController.urlString = "myUrl"
let navController = (UIApplication.shared.delegate as! AppDelegate).navController
navController!.pushViewController(myWebViewController, animated: true)
Here is my redacted target view controller code. The URL to load is set in the menu.
import UIKit
class MyWebViewController: UIViewController, UIWebViewDelegate {
var urlString: String = "invalid url"
var url: URL {
return URL(string: urlString)!
}
var urlRequest: URLRequest {
var urlRequest = URLRequest(url: url)
return urlRequest
}
let webView: UIWebView = {
let view = UIWebView()
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// declare various buttons and stack views
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
contentStackView.addArrangedSubview(webView)
mainStackView.addArrangedSubview(contentStackView)
view.addSubview(mainStackView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
webView.loadRequest(urlRequest as URLRequest)
}
The problem has nothing to do with the web view, the web view controller, or anything else except where you're pushing the view controller. You are saying:
let navController = (UIApplication.shared.delegate as! AppDelegate).navController
navController!.pushViewController(myWebViewController, animated: true)
That is highly unusual. The usual thing is that we are inside a navigation interface already, and we push onto its stack:
let navController = self.navigationController
navController!.pushViewController(myWebViewController, animated: true)
But you are not saying that. Why not? I'm guessing it's because self does not have a navigationController. So you are successfully pushing onto the navigation controller, all right, but you are not seeing anything happening because the navigation controller is behind the scenes — the self view controller's view is blocking it or has replaced it.
And that explains why you never detect the url loading. Your call to webView.loadRequest is in viewWillAppear. But this view will not appear; it is behind the scenes. viewWillAppear is never called. Similarly viewDidLoad is not called, because this view is not going to load; the navigation controller is not occupying the interface — self is.
I could possibly think of one scenario why the webViewController is not being pushed.
The UIViewController from which you are trying to make the push might not be part of the navigation controller hierarchy.
Why don't you try using the code instead of getting the navController from AppDelegate
self.navigationController?.pushViewController(myWebViewController, animated: true)
If this doesn't work out then your are certainly doing something wrong.
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
}
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.