I'm fairly new to Swift development, and I have a hybrid app I am developing that I have wired up authentication to. When a user authenticates with the fingerprint sensor on a device, I want to trigger a JS or otherwise interact with the WKWebView... but for some reason, I can't seem to get it to work. I can do simple things like change the window HREF... but if I do something more complex, it either does nothing, or fails.
Here is the code of my viewController:
import UIKit
import WebKit
import LocalAuthentication
class ViewController: UIViewController, WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate {
@IBOutlet var containerView : UIView! = nil
// @IBOutlet weak var webView: UIWebView!
var webView: WKWebView?
var contentController = WKUserContentController();
@IBOutlet var activityIndicatorView: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
// append the userAgent and ensure it contains our browser detect regEx
let userAgent = UIWebView().stringByEvaluatingJavaScriptFromString("navigator.userAgent")! + " iPad"
NSUserDefaults.standardUserDefaults().registerDefaults(["UserAgent" : userAgent])
// add JS content controller
var config = WKWebViewConfiguration()
config.userContentController = contentController
// instantiate the web view
let webView = WKWebView(frame: CGRectZero, configuration: config)
webView.setTranslatesAutoresizingMaskIntoConstraints(false)
webView.navigationDelegate = self
view.addSubview(webView)
// customize sizing
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[webView]|", options: NSLayoutFormatOptions.allZeros, metrics: nil, views: ["webView": webView]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[webView]|", options: NSLayoutFormatOptions.allZeros, metrics: nil, views: ["webView": webView]))
// open the URL for the app
let urlPath = "http://im_a_url"
let url: NSURL = NSURL(string: urlPath)!
let request = NSURLRequest(URL: url)
webView.loadRequest(request)
}
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
// trigger authentication
authenticateUser()
}
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
}
func logInWithStoredCredentials() {
println("successful auth - log in");
// TO DO - use core data to stor user credentials
webView!.evaluateJavaScript("document.getElementById('anonymousFormSubmit').click();", nil)
}
func authenticateUser() {
// Get the local authentication context.
let context = LAContext()
// Declare a NSError variable.
var error: NSError?
// Set the reason string that will appear on the authentication alert.
var reasonString = "Authentication is needed to access aware360Suite."
// Check if the device can evaluate the policy.
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
[context .evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
if success {
self.logInWithStoredCredentials()
// self.webView?.evaluateJavaScript("document.getElementById('auiAuthSubmitBtn').click();", nil)
}
else{
// If authentication failed then show a message to the console with a short description.
// In case that the error is a user fallback, then show the password alert view.
println(evalPolicyError?.localizedDescription)
switch evalPolicyError!.code {
case LAError.SystemCancel.rawValue:
println("Authentication was cancelled by the system")
case LAError.UserCancel.rawValue:
println("Authentication was cancelled by the user")
case LAError.UserFallback.rawValue:
println("User selected to enter custom password")
// self.showPasswordAlert()
default:
println("Authentication failed")
// self.showPasswordAlert()
}
}
})]
}
else{
// If the security policy cannot be evaluated then show a short message depending on the error.
switch error!.code{
case LAError.TouchIDNotEnrolled.rawValue:
println("TouchID is not enrolled")
case LAError.PasscodeNotSet.rawValue:
println("A passcode has not been set")
default:
// The LAError.TouchIDNotAvailable case.
println("TouchID not available")
}
}
}
}
The issue is in the successful authentication method:
func logInWithStoredCredentials() {
println("successful auth - log in");
// TO DO - use core data to use stored user credentials
webView!.evaluateJavaScript("document.getElementById('anonymousFormSubmit').click();", nil)
}
I cannot seem to get a handle to the webView here. If I attempt to actually evaluate script here, it throws the following error:
2015-02-10 17:07:32.912 A360[2282:462860] -[UIWebView evaluateJavaScript:completionHandler:]: unrecognized selector sent to instance 0x1741897f0
2015-02-10 17:07:32.916 A360[2282:462860] <NSXPCConnection: 0x178103960> connection to service named com.apple.CoreAuthentication.daemon: Warning: Exception caught during decoding of received reply to message 'evaluatePolicy:options:reply:', dropping incoming message and calling failure block.
Exception: -[UIWebView evaluateJavaScript:completionHandler:]: unrecognized selector sent to instance 0x1741897f0
I'm quite at a loss. I know I don't have a proper handle to the WebView here, because I know that if I attempt an operation like this immediately after having had a successful navigation from the view, it will work fine, e.g.:
func webView(webView: WKWebView!, didFinishNavigation navigation: WKNavigation!) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
webView.evaluateJavaScript("document.getElementById('anonymousFormSubmit').click();", nil)
}
So clearly in my logInWithStoredCredentials function it has lost the context of the webView.
How can I get a proper handle to the webView in my logInWithStoredCredentials func?
Sorry all - I know this is a rather basic question, but I have been banging my head against it for hours now, and this is a very pressing issue that I must resolve quickly.
It looks like you have UIWebView instead of WKWebView in your UI:
Exception: -[UIWebView evaluateJavaScript:completionHandler:]: unrecognized selector sent to instance 0x1741897f0
You should instantiate a WKWebView instance instead. Also note that WKWebView cannot be created in Interface Builder, you will have to add it to the view hierarchy at runtime.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With