WKWebview: how to prevent Laravel Page links from opening in safari? - swift

I am new to swift.
I built a web application and serving it as an iOS app using WKWebView (this is for internal company use only).
In the web app, I used laravel pagination, this works properly on a browser and android webview.
what happens on iOS is, when I click the page number link inside the WKWebView, the link opens up in safari. What I want to do is open it on the same Webview.
This is my code inside ViewController.swift
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
var webView : WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = URL(string: "https://domain-online.asia")!
let urlRequest = URLRequest(url: url)
webView.load(urlRequest)
let refresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: webView, action: #selector(webView.reload))
toolbarItems = [refresh]
navigationController?.isToolbarHidden = false
}
override func loadView() {
let source: String = "var meta = document.createElement('meta');" +
"meta.name = 'viewport';" +
"meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';" +
"var head = document.getElementsByTagName('head')[0];" + "head.appendChild(meta);";
let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
let userContentController: WKUserContentController = WKUserContentController()
let conf = WKWebViewConfiguration()
conf.userContentController = userContentController
userContentController.addUserScript(script)
webView = WKWebView(frame: CGRect.zero, configuration: conf)
webView.navigationDelegate = self
view = webView
}
func webView(webView: WKWebView!, createWebViewWithConfiguration configuration: WKWebViewConfiguration!, forNavigationAction navigationAction: WKNavigationAction!, windowFeatures: WKWindowFeatures!) -> WKWebView! {
if navigationAction.targetFrame == nil {
webView.load(navigationAction.request)
}
return nil
}}

Implement decidePolicyFor and set policy as below,
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
guard navigationAction.navigationType == .linkActivated,
let url = navigationAction.request.url,
url.host?.contains("domain-online.asia") == false,
UIApplication.shared.canOpenURL(url) else {
decisionHandler(.allow)
return
}
UIApplication.shared.open(url)
decisionHandler(.cancel)
}

Related

SwiftUI: Cannot open window.open() javascript call on page in wkwebview with ui delegate createWebViewWithConfiguration

I have gone through a lot of different StackOverflow threads & Apple Developer pages to figure out what is going on here. I believe I am so close.
I have a WebView that runs on a site for a bit. Eventually the user can click a link that would open a new tab. It is opened via a window.open() call in JavaScript in the HTML.
However, on SwiftUI nothing runs, and I have set-up what should be the proper UIDelegate. This function never gets entered as I step through the call to WebView.
My question is: How can I make sure the UIDelegate is properly set? Where does this occur? Do I have the right spot?
My implementation (shortened):
struct WebView: UIViewRepresentable {
var url: URL
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
// Enable javascript in WKWebView to interact with the web app
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
preferences.javaScriptCanOpenWindowsAutomatically = true
let configuration = WKWebViewConfiguration()
// Here "iOSNative" is our interface name that we pushed to the website that is being loaded
// configuration.userContentController.add(self.makeCoordinator(), name: "iOSNative")
configuration.preferences = preferences
let webView = WKWebView(frame: CGRect.zero, configuration: configuration)
// webView.navigationDelegate = context.coordinator
// webView.uiDelegate = context.coordinator
// webView.allowsBackForwardNavigationGestures = true
// webView.scrollView.isScrollEnabled = true
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
let request = URLRequest(url: url)
webView.uiDelegate = context.coordinator
webView.navigationDelegate = context.coordinator
webView.load(request)
}
class Coordinator : NSObject, WKUIDelegate, WKNavigationDelegate {
var parent: WebView
var webViewNavigationSubscriber: AnyCancellable? = nil
init(_ uiWebView: WebView) {
// uiWebView.uiDelegate = self
self.parent = uiWebView
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
// Suppose you don't want your user to go a restricted site
if navigationAction.targetFrame == nil {
if let url = navigationAction.request.url {
UIApplication.shared.open(url)
decisionHandler(.allow)
return
}
}
func webView(webView: WKWebView!, createWebViewWith configuration: WKWebViewConfiguration!, forNavigationAction navigationAction: WKNavigationAction!, windowFeatures: WKWindowFeatures!) -> WKWebView! {
// if navigationAction.targetFrame == nil {
// UIApplication.shared.open(navigationAction.request.url!)
// }
// return nil
if (!(navigationAction.targetFrame?.isMainFrame ?? false)) {
webView.load(navigationAction.request)
}
return nil
}
func webView(webView: WKWebView!, createWebViewWithConfiguration configuration: WKWebViewConfiguration!, forNavigationAction navigationAction: WKNavigationAction!, windowFeatures: WKWindowFeatures!) -> WKWebView! {
if navigationAction.targetFrame == nil {
UIApplication.shared.open(navigationAction.request.url!)
}
return nil
}
}
}
My thoughts:
Am I assigning uiDelegate & navigationDelegate in the right spots? It doesn't seem that my createWebViewWith delegate ever gets called. The app opens HTML links in tags with target = "_blank", it's just this one that is called via window.open(x) in javascript.
My resources:
WKWebView and window.open
Window.open() is not working in WKWebView
https://developer.apple.com/forums/thread/664267
https://developer.apple.com/forums/thread/68427

How to Autofill forms in WKWebView Using User Defaults or JavaScript

Hello Swift Developers!.
I am new to swift programming and need little help.
I am developing a very simple app, that should simply load the web using WkWebView and autofill the shipping form it have in on of its page.
I have successfully managed to fetch the page in webView(WkWebView).
First it loads this url https://www.adidas.com/us, after selecting the item in to cart it gets to this delivery page https://www.adidas.com/us/delivery where we have to fill this form. before this, no login information needed.
I am trying to make it done with both UIWebView and WkWebView but to no avail, here's my code, UIWebView part is commented.
class ViewController: UIViewController, WKUIDelegate {
#IBOutlet weak var uiWebView: UIWebView!
var webView: WKWebView!
let url: String = "https://www.adidas.com/us/delivery"
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
//WKWebView programatically so it can run below iOS 11
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
if let safeUrl = URL(string: url){
let request = URLRequest(url: safeUrl)
webView.load(request)
}
// uiWebView.loadRequest(NSURLRequest(url: NSURL(string: url)! as URL) as URLRequest)
// let result = uiWebView.stringByEvaluatingJavaScript(from: "document.title")
// print("result is: \(result!)")
self.webView.evaluateJavaScript("document.getElementById('di-id-cab9a55c-9d253ce3').value = 'Hello'") { (result, error) in
print(result) //This will Print Hello
}
}
// func webViewDidFinishLoad(_ webView: UIWebView) {
//
// let email = defaults.string(forKey: "EMAIL")
// let password = defaults.string(forKey: "Pass")
//
// let fillForm = "document.getElementById('f_707d6a95-3ef9-4b76-a162-9361b4ef7d4d').value = \(password)"
// webView.stringByEvaluatingJavaScript(from: fillForm)
// }
}
And Here's the screenshot of inspect element of First Name field.
After this I would use user default for autofill data. that step I know how to do just stuck here!
Can I have any helpful code snippet or suggestion please? I am really stuck at this point!
Thanks in advance
First: stop using UIWebView since it's deprecated from one side and stop supporting by AppStore from Dec 2020.
You should set navigationDelegate to you WKWebView and set your values to the fields on webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) when the page is loaded e.g.:
override func viewDidLoad() {
super.viewDidLoad()
...
webView?.navigationDelegate = self
...
}
extension ViewController : WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// Set a delay for dynamic pages
//DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let fillForm = "document.getElementsByName('firstName')[0].value = 'My Name'"
webView.evaluateJavaScript(fillForm, completionHandler: nil)
//}
}

Some tweet links does not work when using WKWebView to display a embedded twitter timeline

I'm trying to use WKWebView to display one embedded timeline from Twitter.
Almost all links are working after the WKWebView has loaded the embedded content successfully, but the redirect link for each tweet which displays on the timeline does not.
Here is the embedded tweet link:
https://publish.twitter.com/?query=%40Google&widget=Timeline
This is the embedded twitter timeline:
< a class="twitter-timeline" href="https://twitter.com/Google?ref_src=twsrc%5Etfw">Tweets by Google< /a>
< script async src="https://platform.twitter.com/widgets.js" charset="utf-8">< /script>
If you scroll it down and try to click one of the tweets, you will be lead to the current tweet, but this will not work when you display with WKWebView.
During my test round, I tried to use the same embedded timeline and tested on an Android device which worked.
I think the embedded link is fine and it must be something wrong with how I implemented WKWebView or the WKWebView itself has issues with displaying a Twitter Embedded Timeline.
Here is my code for using WKWebView to display the embedded Twitter timeline:
import UIKit
import WebKit
class ViewController: UIViewController {
private var mWebView: WKWebView!
let embeddedTwittContent = "<a class='twitter-timeline' href='https://twitter.com/Google?ref_src=twsrc%5Etfw'>Tweets by Google</a>"
let scriptValue = "<script async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>"
let redirectLink = "https://twitter.com/Smaforetagarna/status/"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent()
configuration.preferences = preferences
self.mWebView = WKWebView(frame: CGRect.zero, configuration: configuration)
self.mWebView.translatesAutoresizingMaskIntoConstraints = false
self.mWebView.allowsLinkPreview = true
self.mWebView.allowsBackForwardNavigationGestures = true
self.mWebView.navigationDelegate = self
self.view.addSubview(self.mWebView)
// Add Constraints
self.mWebView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
self.mWebView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
self.mWebView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0).isActive = true
self.mWebView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0).isActive = true
loadTweetNews()
}
func loadTweetNews(){
let htmlHeader = "<!DOCTYPE html> <html><meta name=\'viewport\' content=\'initial-scale=1.0\'/> <head> \(scriptValue) </head> <body>"
let htmlFooter = "</body> </html>"
let orderHtml = htmlHeader + embeddedTwittContent + htmlFooter
let url: URL = URL(string: "https:")!
self.mWebView.loadHTMLString(orderHtml, baseURL: url)
}
}
extension ViewController: WKNavigationDelegate{
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("page finished load")
}
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
print("didReceiveServerRedirectForProvisionalNavigation: \(navigation.debugDescription)")
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("didStartProvisionalNavigation")
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if navigationAction.navigationType == .linkActivated {
if let url = navigationAction.request.url,
UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
print(url)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
} else {
decisionHandler(.allow)
}
}
}
If you run this code, you will be able to click almost all links, but as soon you trying to click one of these tweets, nothing will happen.
My project deployment target is 10.0
My Xcode version is 10.2.1
My Swift version is 4.2

WKWebview adjustment to be in the background

I've switched one of my apps to use WKWebview and it's the first time for me, I had a problem which is as you know I can't control it from the storyboard, I have a scrollview inside a sidebar that comes out when you click the button on the upper left, but it seems like it appears in the back of the view, how can I fix this please?
let configuration = WKWebViewConfiguration()
configuration.preferences = preference
webView = WKWebView(frame: view.bounds, configuration: configuration)
view.addSubview(webView)
please note that I already clicked the button in that screeshot but the scroll didn't show]1
Use this,
first import
import WebKit
set delegate of WKWebview
WKScriptMessageHandler , WKNavigationDelegate
declare webview
var webView: WKWebView!
var webConfiguration:WKWebViewConfiguration! = nil
override func loadView() {
webView = WKWebView(frame: .zero, configuration: self.webConfig())
webView.navigationDelegate = self
view = webView
}
viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
/*if let url = URL(string: "http://192.168.1.122/arvee/piyush/mobileViewPages/owner/manager.html") {
let request = URLRequest(url: url)
webView.load(request)
}*/
let url = Bundle.main.url(forResource: "AddAppointment/bookApp", withExtension:"html")
let request = URLRequest(url: url!)
webView.load(request)
}
WKWebview Delegate
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print(error)
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let data = JSON(message.body)
}
func webConfig() -> WKWebViewConfiguration {
//if webConfiguration != false {
webConfiguration = WKWebViewConfiguration()
let userController = WKUserContentController()
userController.add(self, name: "buttonClicked")
userController.add(self, name: "pageLoaded")
webConfiguration.userContentController = userController
//}
return webConfiguration
}

Open UIWebView Links In Safari

I have an iOS app using Swift 3. I have a uiwebview in this app. Whenever a user clicks on a link in the webview, it opens up inside the webview. I need it to where the links open up inside the Safari app on the iPhone. Here's the code:
class VideosViewController: UIViewController {
#IBOutlet var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
var URL = NSURL(string: "http://example.com")
webView.loadRequest(NSURLRequest(url: URL! as URL) as URLRequest)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Can you please give me some code that I can enter? I'm new at coding, so thanks a lot.
UPDATE: Thanks to an answer I got some code, but the links are still not opening in Safari. Here's the code so far...
class VideosViewController : UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView : UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "http://example.com") else { return }
let request = URLRequest(url: url)
webView.loadRequest(request)
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == .linkClicked {
guard let url = request.url else { return true }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
return false
}
return true
}
}
Using UIWebView:
Make sure your view controller conforms to UIWebViewDelegate and then implement this method in the controller:
class VideosViewController : UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView : UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "http://example.com") else { return }
webView.delegate = self
let request = URLRequest(url: url)
webView.loadRequest(request)
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == .linkClicked {
guard let url = request.url else { return true }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
return false
}
return true
}
}
Using WKWebView:
Use a WKWebView instead of a UIWebView and make sure your controller conforms to the WKNavigationDelegate protocol. So your implementation would look something like the following:
class VideosViewController : UIViewController, WKNavigationDelegate {
var webView : WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "http://example.com") else { return }
webView = WKWebView(frame: self.view.frame)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.isUserInteractionEnabled = true
webView.navigationDelegate = self
self.view.addSubview(self.webView)
let request = URLRequest(url: url)
webView.load(request)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
// Check if a link was clicked
if navigationAction.navigationType == .linkActivated {
// Verify the url
guard let url = navigationAction.request.url else { return }
let shared = UIApplication.shared
// Check if opening in Safari is allowd
if shared.canOpenURL(url) {
// Ask the user if they would like to open link in Safari
let alert = UIAlertController(title: "Open link in Safari?", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (alert: UIAlertAction) -> Void in
// User wants to open in Safari
shared.open(url, options: [:], completionHandler: nil)
}))
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
decisionHandler(.cancel)
}
decisionHandler(.allow)
}
}
This way, when a user clicks a link in the web view, they will be prompted with an alert that will ask them whether or not they would like to open it in Safari, and if it is allowed, Safari will launch and the link will be opened.