I've been trying to use WKWebView to fill in a webform and submit. So far I have been able to do that. The problem is the WebView form loop endlessly from "insert element, "trying to submit" to "submited" once I used the below code.
let submitFunction = document.forms[0].submit();
Anyone knows what is happening? How can I rewrite this code?
Thank you very much!
import UIKit
import WebKit
class AnswerVC: UIViewController {
#IBOutlet weak var webView: WKWebView!
var questionText = "1+1"
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
}
override func viewWillAppear(_ animated: Bool) {
let url:URL = URL(string: "https://gamma.sympy.org/")!
let urlRequest:URLRequest = URLRequest(url: url)
webView.load(urlRequest)
}
}
extension AnswerVC: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("insert element")
let scriptFunction = "document.getElementById('id_i').value = '\(questionText)';"
webView.evaluateJavaScript(scriptFunction) { (result, error) in
if error != nil {
ProgressHUD.showError(error as? String)
} else {
print("trying to submit")
let submitFunction = "document.forms[0].submit();"
webView.evaluateJavaScript(submitFunction) { (result, error) in
if error != nil {
print(error?.localizedDescription)
} else {
print("submited")
}
}
}
}
}
}
Related
I´m working on an app that will show a website. But I'm running into errors when loading the website in app. I´ts also working perfectly fine on android.
The error:
WebPageProxy::didFailProvisionalLoadForFrame: frameID = 3, domain =
WebKitErrorDomain, code = 102 Error Domain=WebKitErrorDomain Code=102
"Frame load interrupted"
UserInfo={_WKRecoveryAttempterErrorKey=<WKReloadFrameErrorRecoveryAttempter:
0x600001922120>, NSErrorFailingURLStringKey=https://site.site/login,
NSErrorFailingURLKey=https://site.site/login,
NSLocalizedDescription=Frame load interrupted} Failed Provisinal
Navigation
My code:
// ViewController.swift
import UIKit
import WebKit
import SafariServices
import PushNotifications
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
let pushNotifications = PushNotifications.shared
var webView: WKWebView!
var manifest = WebAppManifest.shared
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage){
if let objStr = message.body as? String {
var arr = [" "," "]
if objStr.contains(";") {
arr = objStr.components(separatedBy: ";")
}
let tokenProvider = BeamsTokenProvider(authURL: "https://site.site/beams/token") { () -> (AuthData) in
let headers : [String: String] = [:]
var queryParams : [String: String] = [:]
if objStr.contains(";") {
queryParams = ["api_token":arr[1]]
}
return AuthData(headers: headers, queryParams: queryParams)
}
if objStr.contains(";") {
self.pushNotifications.setUserId(arr[0], tokenProvider: tokenProvider, completion: { error in
guard error == nil else {
print(error.debugDescription)
return
}
print("Succesfully authenticated with Pusher Beams")
})
} else {
self.pushNotifications.setUserId(objStr, tokenProvider: tokenProvider, completion: { error in
guard error == nil else {
print(error.debugDescription)
return
}
print("Succesfully authenticated with Pusher Beams")
})
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let contentcontroller = WKUserContentController()
contentcontroller.add(self, name: "callbackHandler")
let config = WKWebViewConfiguration()
config.userContentController = contentcontroller
// Set-up the UI
self.view.backgroundColor = UIColor(fromHex: manifest.theme_color)
// Creates the web view engine
webView = WKWebView()
view.addSubview(webView)
webView.navigationDelegate = self
// Display attribute
var guide: AnyObject = self.view.safeAreaLayoutGuide
if manifest.display == "fullscreen" {
guide = self.view
webView.scrollView.contentInsetAdjustmentBehavior = .always
}
// Make the Web View take the whole screen
webView.translatesAutoresizingMaskIntoConstraints = false
webView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
webView.rightAnchor.constraint(equalTo: guide.rightAnchor).isActive = true
webView.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
// It will enable navigation gestures on the web view
webView.allowsBackForwardNavigationGestures = true
let url = URL(string: "https://" + manifest.origin + manifest.start_url)!
print(pushNotifications.getDeviceInterests())
webView.load(URLRequest(url: url))
}
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
get {
switch manifest.orientation {
case "landscape":
return UIInterfaceOrientationMask.landscape
case "portrait":
return UIInterfaceOrientationMask.portrait
default:
return UIInterfaceOrientationMask.all
}
}
set { self.supportedInterfaceOrientations = newValue }
}
override var prefersStatusBarHidden: Bool {
return manifest.display == "fullscreen"
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return UIColor(fromHex: manifest.theme_color).isDark() ? .lightContent : .darkContent
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
view.setNeedsLayout()
}
// MARK: loadWebPage function
func loadWebPage(url: URL) {
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
components?.query = nil
// If already has a query then append a param
// otherwise create a new one
if let query = url.query {
// If isFromMobile param already exists jsut reassign the existing query
if query.contains("source=TWA&platform=iOS") {
components?.query = query
} else {
components?.query = query + "&source=TWA&platform=iOS"
}
} else {
components?.query = "source=TWA&platform=iOS"
}
print(components!.url!)
let customRequest = URLRequest(url: components!.url!)
webView!.load(customRequest)
}
// MARK: NavigationAction handler
// Defines if it should navigate to a new URL
// Logic to check if the destination URL is in the scope
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
print(navigationAction.request.url?.absoluteURL)
if let host = navigationAction.request.url?.host,
let path = navigationAction.request.url?.path{
if host.contains(manifest.origin) &&
path.starts(with: manifest.scope){
let query = navigationAction.request.url?.query
if query != nil{
print("two")
// MARK: Pusher clear state
if path.contains("logout") || path.contains("login") {
pushNotifications.clearAllState {
print("Cleared all state!")
}
}
decisionHandler(.allow)
} else {
print("one")
loadWebPage(url: navigationAction.request.url!)
decisionHandler(.cancel)
}
return
} else {
print("wrong")
// Destination URL is out of the scope
if navigationAction.request.url?.scheme == "http" ||
navigationAction.request.url?.scheme == "https" {
// Opens an In-App browser
decisionHandler(.cancel)
let safariVC = SFSafariViewController(url: navigationAction.request.url!)
safariVC.preferredBarTintColor = UIColor(fromHex: manifest.theme_color)
safariVC.preferredControlTintColor =
UIColor(fromHex: manifest.theme_color).isDark() ? UIColor.white : UIColor.black
present(safariVC, animated: true)
} else {
// It looks like a different protocol
// We ask the OS to open it
UIApplication.shared.open(navigationAction.request.url!)
}
}
} else {
decisionHandler(.cancel)
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
print("error")
if let response = navigationResponse.response as? HTTPURLResponse {
if response.statusCode >= 400 {
print("400 error")
handleError()
}
}
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print(error, "Failed Navigation")
handleError()
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print(error, "Failed Provisinal Navigation")
handleError()
}
// Error handling in case the content of the PWA can't be loaded
func handleError() {
webView.loadHTMLString("<h1>:(</h1>", baseURL: nil)
let alert = UIAlertController(title: "Error", message: "There was a problem loading the App from the Internet. Please check your connection and try again", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: { (action) in
exit(100)
}))
present(alert, animated: true)
}
}
I have already made multiple apps of this type of but never had this error pop up. Any suggestion on how to fix this?
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/7FUgg.png
I want to open a phone number Alertsheet (like + 91-1234567890) when click call button(phone icon).
my image shows page of web, which is called in my app and i have use WebKit in my code.
what i have tried,
i'm new to webkit help me out.
import UIKit
import WebKit
import Alamofire
class ContinueViewController: UIViewController, WKUIDelegate {
func getPostString(params:[String:Any]) -> String{
var data = [String]()
for(key, value) in params
{
data.append(key + "=\(value)")
}
return data.map { String($0) }.joined(separator: "&")
}
#IBOutlet weak var activityView: UIActivityIndicatorView!
#IBOutlet weak var webview: WKWebView!
var url: URL?
var id = 0, skill = 0
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if #available(iOS 14.0, *) {
webview.configuration.defaultWebpagePreferences.allowsContentJavaScript = true
}else {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent()
configuration.preferences = preferences
}
webview.navigationDelegate = self
self.navigationController?.navigationBar.isHidden = true
webview.uiDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
activityView.startAnimating()
guard let url = self.url else { return }
var req = URLRequest(url: url)
let params = ["id" : id,"skill" : skill]
let postString = self.getPostString(params: params)
req.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
req.httpMethod = "POST"
req.httpBody = postString.data(using: .utf8)
self.webview.load(req)
}
}
extension ContinueViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
activityView.stopAnimating()
}
}
In your WKNavigationDelegate implementation, you can do something like the following:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url, url.scheme == "tel" {
UIApplication.shared.open(url)
decisionHandler(.cancel)
}else {
decisionHandler(.allow)
}
}
I have use webkit to show url on my app.
when I click on image button(green button in ), it shows
camera or photo library.
issue is, when click Use photo, the webkit
refresh automatically instead showing on page
I have already give photo usage, media, camera permission in plist file
my code is
import UIKit
import WebKit
import Alamofire
class ContinueViewController: UIViewController, WKUIDelegate {
#IBOutlet weak var activityView: UIActivityIndicatorView!
#IBOutlet weak var webview: WKWebView!
func getPostString(params:[String:Any]) -> String{
var data = [String]()
for(key, value) in params
{
data.append(key + "=\(value)")
}
return data.map { String($0) }.joined(separator: "&")
}
var url: URL?
var traderCategoryId = 0, tradeSkillId = 0
override func viewDidLoad() {
super.viewDidLoad()
webview.navigationDelegate = self
self.navigationController?.navigationBar.isHidden = false
webview.uiDelegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
activityView.startAnimating()
guard let url = self.url else { return }
var req = URLRequest(url: url)
let params = ["id" : id,"trader_skills" : tradeSkillId]
let postString = self.getPostString(params: params)
req.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
req.httpMethod = "POST"
req.httpBody = postString.data(using: .utf8)
self.webview.load(req)
}
}
extension ContinueViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
activityView.stopAnimating()
}
}
extension UIImagePickerController {
open override func viewDidLoad() {
super.viewDidLoad()
// self.modalPresentationStyle = .fullScreen
}
}
issue is, when click Use photo, the webkit refresh automatically instead showing on page
Because you self.webview.load(req) in
override func viewWillAppear(_ animated: Bool) {
shows camera or photo library, then return to this page
func viewWillAppear called again
self.webview.load(req) called again
do it simple ,
put self.webview.load(req) the relevant in
override func viewDidLoad() {
I am working on the following code and trying to show an activity indicator in the view whilst the page is loading..
I tried to implement the WKNavigationDelegate methods but I am failing as nothing shows.
Any suggestions on how to fix this?
I am not setting the SupportWebView view delegate anywhere but I wouldn't know how to do it in swift..
import UIKit
import WebKit
class SupportWebView: UIViewController, WKNavigationDelegate {
#IBOutlet var containerView : UIView? = nil
var webView: WKWebView?
override func loadView() {
super.loadView()
self.webView = WKWebView()
self.view = self.webView
}
override func viewDidLoad() {
super.viewDidLoad()
var dataManager = DataManager.sharedDataManager()
var url = dataManager.myValidURL
var req = NSURLRequest(URL:url!)
self.webView!.loadRequest(req)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
}
As commented, you forgot to set the webView delegate:
override func loadView() {
super.loadView()
self.webView = WKWebView()
self.webView.navigationDelegate = self
self.view = self.webView
}
You should use the delegate methods for all other purposes, but key path monitoring works fine for this one purpose.
Here is a Swift 4 implementation that works fine.
// Somewhere in your view controller
private var loadingObservation: NSKeyValueObservation?
private lazy var loadingIndicator: UIActivityIndicatorView = {
let spinner = UIActivityIndicatorView()
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.color = .black
return spinner
}()
override func viewDidLoad() {
super.viewDidLoad()
// Setup...
loadingObservation = webView.observe(\.isLoading, options: [.new, .old]) { [weak self] (_, change) in
guard let strongSelf = self else { return }
// this is fine
let new = change.newValue!
let old = change.oldValue!
if new && !old {
strongSelf.view.addSubview(strongSelf.loadingIndicator)
strongSelf.loadingIndicator.startAnimating()
NSLayoutConstraint.activate([strongSelf.loadingIndicator.centerXAnchor.constraint(equalTo: strongSelf.view.centerXAnchor),
strongSelf.loadingIndicator.centerYAnchor.constraint(equalTo: strongSelf.view.centerYAnchor)])
strongSelf.view.bringSubview(toFront: strongSelf.loadingIndicator)
}
else if !new && old {
strongSelf.loadingIndicator.stopAnimating()
strongSelf.loadingIndicator.removeFromSuperview()
}
}
}
Please, below code which is working fine[Swift 4.2].
#IBOutlet weak var wv: WKWebView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
loadYoutube(videoID: "KqNS7uAvOxk")
}
Now load Youtube Video
func loadYoutube(videoID:String) {
guard let youtubeURL = URL(string: "https://www.youtube.com/embed/\(videoID)")
else { return }
wv.load( URLRequest(url: youtubeURL) )
wv.navigationDelegate = self
}
Implement below this function:
func showActivityIndicator(show: Bool) {
if show {
activityIndicator.startAnimating()
} else {
activityIndicator.stopAnimating()
}
}
Implement below these three delegate method:
func webView(_ webView: WKWebView, didFinish navigation:
WKNavigation!) {
showActivityIndicator(show: false)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation
navigation: WKNavigation!) {
showActivityIndicator(show: true)
}
func webView(_ webView: WKWebView, didFail navigation:
WKNavigation!, withError error: Error) {
showActivityIndicator(show: false)
}
Let me know if it is not working.
Technologies: Swift, iOS8.1.3, XCode 6, Maverick
I've added a "rewind" back button to my UIWebview via the main.storyboard and connected it here on my view with a backButton func:
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView: UIWebView!
var detailItem: NSURL?
var grabData = false
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
self.navigationController?.toolbarHidden = false;
}
#IBAction func backButton(sender: AnyObject) {
self.webView.goBack()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if let url = self.detailItem {
webView.loadRequest(NSURLRequest(URL: url))
}
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request:
NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if grabData {
grabData = false
return true
} else {
grabData = true
manuallyLoadPage(request)
return false
}
}
func manuallyLoadPage(request: NSURLRequest) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
(data, response, error) invar html = NSString(data: data, encoding: NSUTF8StringEncoding) as String
html = html.stringByReplacingOccurrencesOfString("</head>", withString: "<styles><link rel='stylesheet' type='text/css' href='link-to-styles.css' media='all'></styles></head>", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
self.webView.loadHTMLString(html, baseURL: response.URL!)
}
task.resume()
}
My back button doesn't do anything when clicked and I want it to show the previous webpage. I think it has something to do with how I am intercepting the session and manually loading the WebView. Maybe it doesn't know what the original base url is? Something else?