How to submit a form in WKWebView Swift? - ios

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

Error Domain=WebKitErrorDomain Code=102 "Frame load interrupted" iOS WKwebview WKnavigationdelegate

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?

How to show call dialog when click on action tag?

[![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)
}
}

How to Handle Image uploading in WKWebview?

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() {

Displaying activity indicator on WKWebView using swift

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.

Back button not working for my manually loaded UIWebView for external website

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?

Resources