3

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.

1
  • Glance at that new library, it may help you ;) Commented Jun 2, 2015 at 20:55

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

Comments

1

You have var webView: WKWebView? in your class but then redefine it in viewDidLoad as let webView = WKWebView(frame: CGRectZero, configuration: config) Remove the let in front of the webView and it should work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.