Swift WKWebView webViewDidClose(window.close()) not working - swift

I have a WKWebView
after moving several pages, web do confirm action.
like this:
var Common = {
Close : function (){
if(confirm("confirm?")) window.close();
}
}
in my code:
webView.uiDelegate = self
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
// this method is not invoked
}
func webViewDidClose(_ webView: WKWebView) {
dismiss(animated: true, completion: nil)
}
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: #escaping (Bool) -> Void) {
// this method invoked
let alertController = UIAlertController(title: message, message: nil, preferredStyle: .alert);
let cancelAction = UIAlertAction(title: "cancel", style: .cancel) {
_ in completionHandler(false)
}
let okAction = UIAlertAction(title: "ok", style: .default) {
_ in completionHandler(true)
}
alertController.addAction(cancelAction)
alertController.addAction(okAction)
DispatchQueue.main.async {
self.present(alertController, animated: true, completion: nil)
}
}
in my alertController I called completionHandler(true). but webViewDidClose is not invoked. so I can not dismiss web vc.
what is the problem?
What can I try?

// webView.load(URLRequest)
webView.evaluateJavaScript("window.open(\"\(url.absoluteString)\");", completionHandler: { result, error in
})
override func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if let currentWebView = createWebView {
currentWebView.load(navigationAction.request)
return nil
}
createWebView = WKWebView(frame: view.bounds, configuration: configuration)
createWebView!.navigationDelegate = self
createWebView!.uiDelegate = self
view.addSubview(createWebView!)
return createWebView!
}

Related

WKWebView not displaying after successful load, only seeing black screen

I'm new to swift & am attempting to open a login page, login, use the WKNavigationDelegate to get the redirect uri and save the response. I was able to open the url using UIApplication.shared.open but attempting to hit the url using WKWebview, I am getting a loaded response but no view appears.
Am I not declarling the view correctly or is WKWebView not supposed to be used in that manner? This is the code i'm running, its building succesfully and responding that its 'loaded'. Any help would be be appreciated thanks.
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
// The webview
var webViewObj: WKWebView!
var webNav: WKNavigation!
// this calls the dbhelper to create a db and a table
let db = DBHelper()
var list = [DBGrade]()
override func loadView() {
super.loadView()
let webConfiguration = WKWebViewConfiguration()
webViewObj = WKWebView(frame: .zero, configuration: webConfiguration)
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://www.hackingwithswift.com")
let request = URLRequest(url: url!)
webViewObj.navigationDelegate = self
self.webViewObj.uiDelegate = self
webViewObj.load(request)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("loaded")
}
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
webView.load(navigationAction.request)
}
return nil
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
let hostStr = String(describing: navigationAction.request.url)
let host = hostStr
if host.range(of: "hackingwithswift.com") != nil {
decisionHandler(.allow)
print("did it")
return
}
else{
decisionHandler(.cancel)
}
}
}
So moving my delcaration of 'self.webViewObj.uiDelegate = self' & adding 'view = webViewObj' to the loadView() function worked & I'm able to see the webpage appear.

ios: How to reload webview from a Coordinator class SwiftUi

I am making an app that contains multiple web-view's. I am wanting to reload my web-view from the coordinator class after a JS command is sent from the webpage and inside the "func processReturnedJS(body: String)" shown below. I am wondering how I can achieve this in a simple way?
Thanks
SubscriptionViewController:
import SwiftUI
import WebKit
struct SubscriptionViewController: UIViewRepresentable {
#StateObject var storeManager: StoreManager
let frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
let config = WKWebViewConfiguration()
let cookies = HTTPCookieStorage.shared.cookies ?? []
func makeUIView(context: UIViewRepresentableContext<SubscriptionViewController>) -> WKWebView {
for cookie in cookies {
config.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
}
let preferences = WKPreferences()
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
let userContentController = WKUserContentController()
userContentController.add(context.coordinator, name:"observer")
configuration.userContentController = userContentController
let view = WKWebView(frame: frame, configuration: configuration)
view.navigationDelegate = context.coordinator
DispatchQueue.main.async {
let url = URL(string:"theurl")!
let request = URLRequest(url: url)
for cookie in cookies {
config.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
view.configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
}
view.load(request)
}
return view
}
func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<SubscriptionViewController>) {
}
func makeCoordinator() -> CoordinatorSubscription {
CoordinatorSubscription(self, storeManage: self.storeManager)
}
typealias UIViewType = WKWebView
}
class CoordinatorSubscription: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
var control: SubscriptionViewController
var storeManage: StoreManager
init(_ control: SubscriptionViewController, storeManage: StoreManager) {
self.control = control
self.storeManage = storeManage
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
processReturnedJS(body: message.body as! String)
}
func sendjs(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
}
func processReturnedJS(body: String) {
print("kkkk")
//want to reload webview from here
}
}
You can store a reference to the WKWebView in your coordinator:
class CoordinatorSubscription: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
var control: SubscriptionViewController
var storeManage: StoreManager
var webView : WKWebView? //<-- Here
init(_ control: SubscriptionViewController, storeManage: StoreManager) {
self.control = control
self.storeManage = storeManage
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
processReturnedJS(body: message.body as! String)
}
func sendjs(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
}
func processReturnedJS(body: String) {
self.webView?.reload() //here
}
}
Then, make sure to set that property in makeUIView:
func makeUIView(context: UIViewRepresentableContext<SubscriptionViewController>) -> WKWebView {
for cookie in cookies {
config.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
}
let preferences = WKPreferences()
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
let userContentController = WKUserContentController()
userContentController.add(context.coordinator, name:"observer")
configuration.userContentController = userContentController
let view = WKWebView(frame: frame, configuration: configuration)
view.navigationDelegate = context.coordinator
DispatchQueue.main.async {
let url = URL(string:"theurl")!
let request = URLRequest(url: url)
for cookie in cookies {
config.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
view.configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
}
view.load(request)
}
context.coordinator.webView = view //<-- Here
return view
}

SwiftUI runJavaScriptConfirmPanelWithMessage does not work with WKWebView

I'm building a simple webapp with SwiftUI that simply load my website into the webview. From my website, it has to show the Javascript confirm for users to able to logout from the app and my function inside of runJavaScriptConfirmPanelWithMessage crashes the app when Javascript confirm is triggered.
Here is the entire code of my webapp.
import SwiftUI
import WebKit
struct Webview : UIViewRepresentable {
let request: URLRequest
var webview: WKWebView?
init(web: WKWebView?, req: URLRequest) {
self.webview = WKWebView()
self.request = req
}
class Coordinator: NSObject, WKUIDelegate {
var parent: Webview
init(_ parent: Webview) {
self.parent = parent
}
// Delegate methods go here
func webView(_ webView: WKWebView,
runJavaScriptAlertPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo,
completionHandler: #escaping () -> Void) {
// alert functionality goes here
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
alertController.present(alertController, animated: true)
completionHandler()
}
func webView(_ webView: WKWebView,
runJavaScriptConfirmPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo,
completionHandler: #escaping (Bool) -> Void) {
// confirm functionality goes here. THIS CRASHES THE APP
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
completionHandler(true)
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
completionHandler(false)
}))
alertController.present(alertController, animated: true, completion: nil)
}
func webView(_ webView: WKWebView,
runJavaScriptTextInputPanelWithPrompt prompt: String,
defaultText: String?,
initiatedByFrame frame: WKFrameInfo,
completionHandler: #escaping (String?) -> Void) {
// prompt functionality goes here
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
return webview!
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.uiDelegate = context.coordinator
uiView.load(request)
}
func goBack(){
webview?.goBack()
}
func goForward(){
webview?.goForward()
}
func reload(){
webview?.reload()
}
}
struct ContentView: View {
let webview = Webview(web: nil, req: URLRequest(url: URL(string: "https://google.com")!))
var body: some View {
VStack {
webview
HStack() {
Button(action: {
self.webview.goBack()
}){
Image(systemName: "chevron.left")
}.padding(32)
Button(action: {
self.webview.reload()
}){
Image(systemName: "arrow.clockwise")
}.padding(32)
Button(action: {
self.webview.goForward()
}){
Image(systemName: "chevron.right")
}.padding(32)
}.frame(height: 40)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I'm not sure what I did wrong in this section for runJavaScriptConfirmPanelWithMessage that crashes the app.
Here is the part that I'm having a trouble with.
func webView(_ webView: WKWebView,
runJavaScriptConfirmPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo,
completionHandler: #escaping (Bool) -> Void) {
// confirm functionality goes here. THIS CRASHES THE APP
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
completionHandler(true)
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in
completionHandler(false)
}))
alertController.present(alertController, animated: true, completion: nil)
}
Can anyone tell me what I'm doing wrong here?
Please note that you're trying to present alertController from alertController and that's a crash reason.
You need a View Controller to present alert. Try this:
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: #escaping (Bool) -> Void) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
alertController.addAction(
UIAlertAction(title: "OK", style: .default, handler: { (action) in completionHandler(true) })
)
alertController.addAction(
UIAlertAction(title: "Cancel", style: .default, handler: { (action) in completionHandler(false) })
)
if let controller = topMostViewController() {
controller.present(alertController, animated: true, completion: nil)
}
}
private func topMostViewController() -> UIViewController? {
guard let rootController = keyWindow()?.rootViewController else {
return nil
}
return topMostViewController(for: rootController)
}
private func keyWindow() -> UIWindow? {
return UIApplication.shared.connectedScenes
.filter {$0.activationState == .foregroundActive}
.compactMap {$0 as? UIWindowScene}
.first?.windows.filter {$0.isKeyWindow}.first
}
private func topMostViewController(for controller: UIViewController) -> UIViewController {
if let presentedController = controller.presentedViewController {
return topMostViewController(for: presentedController)
} else if let navigationController = controller as? UINavigationController {
guard let topController = navigationController.topViewController else {
return navigationController
}
return topMostViewController(for: topController)
} else if let tabController = controller as? UITabBarController {
guard let topController = tabController.selectedViewController else {
return tabController
}
return topMostViewController(for: topController)
}
return controller
}
Thanks to this answer https://stackoverflow.com/a/57877120/11212894

I'm trying to open external links from my app but it won't open

I have a wkwebview app, I need help with my code, I can't seems to open external links from my website inside my app.
import UIKit
import WebKit
import UserNotifications
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
var webView: WKWebView!
var activityIndicator: UIActivityIndicatorView!
var bgImage: UIImageView!
var urlString = ""
override func loadView() {
super.loadView()
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
}
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
let vc = ViewController()
vc.urlString = navigationAction.request.url?.absoluteString ?? "https://mywebsite"
vc.view.frame = UIScreen.main.bounds
vc.webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
navigationController?.pushViewController(vc, animated: false)
return vc.webView
}
return nil
}
override var prefersStatusBarHidden: Bool{
return true
}
override func viewDidLoad() {
super.viewDidLoad()
let image : UIImage = UIImage(named:"bgx1")!
bgImage = UIImageView(image: image)
bgImage.frame = CGRect(x: 0, y: 0, width: 1000, height: 2000)
view.addSubview(bgImage)
let myURL = URL(string: "https://mywebsite/")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
webView.allowsBackForwardNavigationGestures = true
activityIndicator = UIActivityIndicatorView()
activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
self.activityIndicator.center = CGPoint(x:self.view.bounds.size.width/2.0,y: self.view.bounds.size.height/2.0);
activityIndicator.autoresizingMask = (UIView.AutoresizingMask(rawValue: UIView.AutoresizingMask.RawValue(UInt8(UIView.AutoresizingMask.flexibleRightMargin.rawValue) | UInt8(UIView.AutoresizingMask.flexibleLeftMargin.rawValue) | UInt8(UIView.AutoresizingMask.flexibleBottomMargin.rawValue) | UInt8(UIView.AutoresizingMask.flexibleTopMargin.rawValue))))
activityIndicator.hidesWhenStopped = true
activityIndicator.style = UIActivityIndicatorView.Style.whiteLarge
activityIndicator.color = UIColor.darkGray
self.view.addSubview(activityIndicator)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
activityIndicator.startAnimating()
bgImage.startAnimating()
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print("It is an error")
activityIndicator.stopAnimating()
bgImage.stopAnimating()
let alert = UIAlertController(title: "Network Error", message: "You have no internet connection", preferredStyle: .alert)
let restartAction = UIAlertAction(title: "Reload page", style: .default, handler: { (UIAlertAction) in
self.viewDidLoad()
})
alert.addAction(restartAction)
present(alert, animated: true, completion: nil)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
activityIndicator.stopAnimating()
bgImage.stopAnimating()
bgImage.isHidden = true
}
}
What am I doing wrong? Please help.
You have to implement the webView(_:decidePolicyFor:decisionHandler:) function of WKNavigationDelegate protocol in order to allow following the links in your web view.
Here is the documentation
please use this webview as I shown in image
code:-
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self;
webView.loadRequest(URLRequest.init(url: URL(string: "https://mywebsite")!));
}
func webViewDidStartLoad(_ webView: UIWebView) {
SVProgressHUD.setDefaultMaskType(SVProgressHUDMaskType.custom)
SVProgressHUD.show();
}
func webViewDidFinishLoad(_ webView: UIWebView) {
SVProgressHUD.dismiss();
}

Swift ShouldStartLoadWith not working when detect url

My application contains Webview To Show Payment Form and when we have done with filling data we can push Done button then Webview will redirect to other Url. I've already searched for this function and I found shouldStartLoadWith function to solve this problem but when I'm implementing this, it's not working
Here is my Code:
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if request.url?.absoluteString == "https://xyzbank.com" {
print("SUCCESS")
return false
}
print("NOT SUCCESS")
return true
}
It's not print either of them.
Here is the rest of my code: I think that i've already called the delegate method of webview but it's still not working
override func viewDidLoad() {
super.viewDidLoad()
hud.textLabel.text = "Processing..."
hud.show(in: self.view)
let mpayment: Payment = params.payment
mywebview.navigationDelegate = self
//Showing webview url....
}
func webView(webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: NSError) {
print(error.localizedDescription)
}
func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("Start to load")
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
print("DID FINISH NAVIGATION")
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.hud.textLabel.text = "Success"
self.hud.detailTextLabel.text = nil
self.hud.dismiss(animated: true)
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if request.url?.absoluteString == "https://example.com" {
print("SUCCESS")
return false
}
print("NOT SUCCESS")
return true
}
Actually, as i seen your webview is using the newer version WKWebview but shouldStartLoadWith function is using for UiWebview instead, For WkWebview is we should using with decisionHandler function.
The code should be look like this:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping ((WKNavigationActionPolicy) -> Void)) {
print("webView:\(webView) decidePolicyForNavigationAction:\(navigationAction) decisionHandler:\(decisionHandler)")
if let url = navigationAction.request.url {
print(url.absoluteString)
if url.absoluteString.hasPrefix("https://example.com"){
print("SUCCESS")
}
}
decisionHandler(.allow)
}