I am trying to get a mobile webpage in an iOS app. Unfortunately the web view is not executing any javascript. After googling a while it turns out that apple restricts local javascript.
Any suggestions how to make it work anyway?
import UIKit
import WebKit
import AVFoundation
class ViewController: UIViewController ,WKUIDelegate, WKNavigationDelegate {
var webView : WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let webView = WKWebView()
let htmlPath = Bundle.main.path(forResource: "Webpage/index", ofType: "html")
let htmlUrl = URL(fileURLWithPath: htmlPath!, isDirectory: false)
webView.loadFileURL(htmlUrl, allowingReadAccessTo: htmlUrl)
webView.navigationDelegate = self
view = webView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
// WKNavigationDelegate
func webView(webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: NSError) {
print(error.localizedDescription)
}
func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("Loading")
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
print("Loaded")
}
Related
I want javascript to send a message back to my WKWebView object, but I get nothing in response. I know the script is running, as the color changes, but I am expecting to also see "trigger from JS" printed in the console, which I don't. If I run the html in Chrome, the javascript console says "Cannot read property 'messageHandlers' of undefined". If I build for iOS (using UIViewRepresentable, MakeUIView and UpdateUIView) the result is the same. If anyone can spot what I have missed I would greatly appreciate it.
This is the entirety of the code:
import SwiftUI
import WebKit
class HtmlData {
let html = """
<!DOCTYPE html>
<html>
<body>
<button onclick="sendMessage()">Send Message</button>
<script>
function sendMessage() {
document.body.style.backgroundColor = "red";
window.webkit.messageHandlers.testMessage.postMessage("trigger from JS");
}
</script>
</body>
</html>
"""
}
struct ContentView: View {
let htmlData = HtmlData()
var body: some View {
JSWebView(html: htmlData.html)
}
}
struct JSWebView: NSViewRepresentable {
let html: String
func makeNSView(context: Context) -> WKWebView {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
return WKWebView()
}
func updateNSView(_ view: WKWebView, context: Context) {
let userContentController = WKUserContentController()
let handler = ContentController(view)
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
configuration.userContentController.add(handler, name: "testMessage")
view.loadHTMLString(html, baseURL: Bundle.main.bundleURL)
}
class ContentController: NSObject, WKScriptMessageHandler {
var parent: WKWebView?
init(_ parent: WKWebView?) {
self.parent = parent
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.body)
}
}
}
Configuration must be passed into the constructor of WKWebView. It can't be set after initialization.
struct JSWebView: NSViewRepresentable {
let html: String
func makeNSView(context: Context) -> WKWebView {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let handler = MessageHandler()
let configuration = WKWebViewConfiguration()
configuration.userContentController.add(handler, name: "testMessage")
return WKWebView(frame: .zero, configuration: configuration)
}
func updateNSView(_ view: WKWebView, context: Context) {
view.loadHTMLString(html, baseURL: Bundle.main.bundleURL)
}
class MessageHandler: NSObject, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.body)
}
}
}
Please tell me, there is an ios application, it contains WebKitView, and in it there is a site with this code:
<div id="test" style="height: 40px; width: 100px; background-color: powderblue;">Hello</div>
<script type="text/javascript">
document.getElementById("test").addEventListener("click", function () {
window.webkit.messageHandlers.test.postMessage("TEXT");
});
</script>
Application Code:
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://site")!
webView.load(URLRequest(url: url))
let config: WKWebViewConfiguration = WKWebViewConfiguration()
config.userContentController.add(self, name: "test")
webView = WKWebView(frame: self.view.frame, configuration: config)
webView?.navigationDelegate = self
self.webView?.load(URLRequest(url:url))
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "test", let messageBody = message.body as? String {
print(messageBody)
}
}
}
When you click on the div in xcode, the following error is displayed in the log
[general] Connection to daemon was invalidated
What could be the problem?
Sorry for my English.
I've added the shared HTML & javascript code in the main bundle as "example.html" and made changes in your code. It is working fine at my end.
override func viewDidLoad() {
super.viewDidLoad()
//Url from the html
let url = URL(fileURLWithPath: Bundle.main.path(forResource: "example", ofType: "HTML") ?? "")
// Configuring WKEWebview
let config: WKWebViewConfiguration = WKWebViewConfiguration()
config.userContentController.add(self, name: "test")
webView = WKWebView(frame: self.view.frame, configuration: config)
webView?.navigationDelegate = self
//Constraints related changes
webView.translatesAutoresizingMaskIntoConstraints = true
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(self.webView)
// Load file
self.webView?.loadFileURL(url, allowingReadAccessTo: Bundle.main.bundleURL)
}
You can load your webpage using
self.webView.load(URLRequest(url: "https://www.site"))
Try it and let me know if you face any issue.
I'm developing IOS App using Webkit, Swift.
And I wanna close view controller which shows webkit when my webview logic is done. Using WKUserContentController, I implemented logic.
But It didn't work.
Below is my code.
This is Swift.
import UIKit
import WebKit
class SelfAuthVC: UIViewController, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler {
private var webView: WKWebView!
let contentController = WKUserContentController()
let config = WKWebViewConfiguration()
override func viewWillAppear(_ animated: Bool) {
contentController.add(self as WKScriptMessageHandler, name: "callbackHandler")
config.userContentController = contentController
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
config.preferences = preferences
webView = WKWebView(frame: view.bounds, configuration: config)
view.addSubview(webView)
let url = Bundle.main.url(forResource: "auth", withExtension: "html")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
webView.navigationDelegate = self
webView.uiDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
}
func webViewDidClose(_ webView: WKWebView) {
self.navigationController?.popViewController(animated: true)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("var my_email = '\(UserInfo.shared().email!)'") { (result, error) in
guard error == nil else {
print(error)
return
}
}
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("didReceive message here")
if(message.name == "callbackHandler"){
print("callbackHandler: \(message.body)")
self.dismiss(animated: true, completion: nil)
self.navigationController?.popViewController(animated: true)
}
}
}
Below is JS
<body>
<script language="JavaScript">
IMP.certification({
min_age: 18
}, function(rsp) {
if ( rsp.success ) {
$.ajax({
method : "POST",
url : "https://******/user/iamport",
dataType : 'json',
data : {
imp_uid : rsp.imp_uid,
user_email: my_email
}
}).done(function(rsp) {
// 이후 Business Logic 처리하시면 됩니다.
(function finish(){
window.webkit.messageHandlers.callbackHandler.postMessage("trigger from JS");
try {
webkit.messageHandlers.callbackHandler.postMessage(
'done'
);
} catch(err) {
console.log('The native context does not exist yet');
}
}())
});
} else {
// Auth failed
var msg = '인증에 실패하였습니다. 관리자에게 문의하세요';
msg += '에러내용 : ' + rsp.error_msg;
alert(msg);
}
});
</script>
</body>
I think I wrote codes that is needed.
But after authorization process, ViewContoller don't disappear.
What changes are needed to solve this problem?
I forgot to add this codes.
config.userContentController = contentController
contentController.add(self, name: "authAction")
I've been working on a simple app that makes a webview of a website where students can take exams.
So basically the problem I have is that when students are done, they have to click on a button that will send the answers.
A popup will appear to make them confirm.
https://i.stack.imgur.com/5GhB8.png
Except that it doesn't show up. Nothing happens when the button is pushed.
It works flawlessly on Safari and I've noticed that it works on the deprecated webview (UIWebview) but I've been trying to make it work on WKWebView.
I'm definitely no swift expert so I apologize if the answer is easy. I've been trying to find some answers about my problem but I'm not sure how to implement it.
Thank you in advance for any help,
import UIKit
import WebKit
class webViewController: UIViewController {
#IBOutlet weak var webview: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let lien = "***"
if let url = URL(string: lien) {
let request = URLRequest(url: url)
_ = webview.load(request);
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I also faced similar issue, mine was popup for connecting facebook won't show in WKWebView but works fine on safari browser.
This code was causing the issue.
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
//This condition was causing the problem while trying to get popup
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
I changed it to following code and it worked
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (navigationAction.targetFrame == nil) {
NSURL *tempURL = navigationAction.request.URL;
NSURLComponents *URLComponents = [[NSURLComponents alloc] init];
URLComponents.scheme = [tempURL scheme];
URLComponents.host = [tempURL host];
URLComponents.path = [tempURL path];
if ([URLComponents.URL.absoluteString isEqualToString:#"https://example.com/Account/ExternalLogin"]) {
WKWebView *webViewtemp = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
webViewtemp.UIDelegate = self;
webViewtemp.navigationDelegate = self;
[self.view addSubview:webViewtemp];
return webViewtemp;
} else {
[webView loadRequest:navigationAction.request];
}
}
return nil;
}
Swift version:
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
let tempURL = navigationAction.request.url
var components = URLComponents()
components.scheme = tempURL?.scheme
components.host = tempURL?.host
components.path = (tempURL?.path)!
if components.url?.absoluteString == "https://example.com/Account/ExternalLogin" {
let webViewtemp = WKWebView(frame: self.view.bounds, configuration: configuration)
webViewtemp.uiDelegate = self
webViewtemp.navigationDelegate = self
self.view.addSubview(webViewtemp)
return webViewtemp
} else {
webView.load(navigationAction.request)
}
}
return nil
}
Hope this helps you
My solution was implementing the WKUIDelegate and adding the functions for the different scenarios (alerts) that the web view may present:
extension WKWebViewController: WKUIDelegate {
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: #escaping () -> Void) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "OK button"), style: .default, handler: { (action) in
completionHandler()
}))
present(alertController, animated: true, completion: nil)
}
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: NSLocalizedString("OK", comment: "OK button"), style: .default, handler: { (action) in
completionHandler(true)
}))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Cancel button"), style: .default, handler: { (action) in
completionHandler(false)
}))
present(alertController, animated: true, completion: nil)
}
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: #escaping (String?) -> Void) {
let alertController = UIAlertController(title: nil, message: prompt, preferredStyle: .actionSheet)
alertController.addTextField { (textField) in
textField.text = defaultText
}
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "OK button"), style: .default, handler: { (action) in
if let text = alertController.textFields?.first?.text {
completionHandler(text)
} else {
completionHandler(defaultText)
}
}))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "Cancel button"), style: .default, handler: { (action) in
completionHandler(nil)
}))
present(alertController, animated: true, completion: nil)
}
}
When web view demands the new popup window than UIDelegate of WKWebView will come in picture.
Follow these steps to show POP-Up view in WKWebView
Take a another web-view
private var popupWebView: WKWebView?
Assign the uiDelegate of your main web-view to self
webView?.uiDelegate = self
Confirm the WKUIDelegate of to your controller
extension PaisaPalVC: WKUIDelegate {
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
popupWebView = WKWebView(frame: view.bounds, configuration: configuration)
popupWebView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
popupWebView?.navigationDelegate = self
popupWebView?.uiDelegate = self
if let newWebview = popupWebView {
view.addSubview(newWebview)
}
return popupWebView ?? nil
}
func webViewDidClose(_ webView: WKWebView) {
webView.removeFromSuperview()
popupWebView = nil
}
}
Hope this helps you😊
I tried the other way round and it worked for me. Here is the code:
Create a instance of WebView
fileprivate var webView: WKWebView?
Initialize instance and make assign it to view.
override func loadView() {
webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
webView?.uiDelegate = self
webView?.navigationDelegate = self
view = webView
}
Thereafter, just add the following delegate method:
func webView(_: WKWebView, createWebViewWith _: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures _: WKWindowFeatures) -> WKWebView? {
self.webView?.load(navigationAction.request)
return nil
}
Voila! Everything will work same as UIWebView.
Note: You won't be able to tap few links on the WebSite, because they would be HTTP instead of HTTPS. WKWebView by default blocks all HTTP requests which aren't secure.
To bypass, just add NSExceptionAllowsInsecureHTTPLoads as true in info.plist file.
I am new to Swift/iOS and have been working on an app that calls Swift code from JavaScript. There are not as many tutorials online and a lot of them are from when Swift was still in beta. Anyway, I was getting an error on my code and I am unable to compile it and I was wondering if any one had any tips and best practices when it comes to calling swift code from JavaScript.
Here is my code
import UIKit
import WebKit
class ViewController: UIViewController, WKScriptMessageHandler {
#IBOutlet var containerView : UIView! = nil
var webView: WKWebView?
override func loadView() {
super.loadView()
var contentController = WKUserContentController();
var userScript = WKUserScript(
source: "redHeader()",
injectionTime: WKUserScriptInjectionTime.AtDocumentEnd,
forMainFrameOnly: true
)
contentController.addUserScript(userScript)
contentController.addScriptMessageHandler(
self,
name: "callbackHandler"
)
var config = WKWebViewConfiguration()
config.userContentController = contentController
self.webView = WKWebView(frame: self.view.frame, configuration: config)
self.view = self.webView!
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var url = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("index", ofType: "html")!)
var req = NSURLRequest(URL: url!)
self.webView!.loadRequest(req)
}
func userContentController(userContentController: WKUserContentController!,didReceiveScriptMessage message: WKScriptMessage!) {
if(message.name == "callbackHandler") {
println("JavaScript is sending a message \(message.body)")
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The error is on class ViewController: UIViewController, WKScriptMessageHandler { and it says
Type 'ViewController' does not conform to protocol 'WKScriptMessageHandler'.
Any help would be greatly appreciated.
I note that your method userContentController is:
func userContentController(userContentController: WKUserContentController!,
didReceiveScriptMessage message: WKScriptMessage!)
it should be:
func userContentController(userContentController: WKUserContentController,
didReceiveScriptMessage message: WKScriptMessage)
(no optionals ! in prototype).
If you need more help, please post your index.html source file