【问题标题】:How to get Swift to interact with a Webview?如何让 Swift 与 Webview 交互?
【发布时间】:2023-03-03 03:42:02
【问题描述】:

我正在使用 Xcode 构建一个基本的 iOS 应用程序,其中主要包含一个带有我的网络应用程序的 web 视图。

我想知道是否有一种不错的方法可以在登录时将用户名保存到设备存储中,以便下次打开应用程序时可以自动输入。由于该应用程序是一个 webview,我不相信有办法让用户保持登录状态(就像其他主要应用程序一样,例如 Facebook),所以我认为自动填写用户名对他们有利。

我发现 this question and answer 可能会解决我的问题,尽管它在良好的 ol'Objective C 中。

我目前的尝试,完全没有任何作用:

let savedUsername = "testusername"

let loadUsernameJS = "document.getElementById(\"mainLoginUsername\").value = " + savedUsername + ";"

self.Webview.stringByEvaluatingJavaScriptFromString(loadUsernameJS)

这对 Swift 来说有可能吗?

【问题讨论】:

标签: ios xcode swift webview


【解决方案1】:

要存储密码,您应该使用钥匙串,特别是 Web 凭据。如果操作正确,这将允许您的应用使用通过 Safari 输入的任何现有钥匙串条目,如果通过您的应用保存,还允许 Safari 访问密码。

下面提供的设置和检索代码:

private let domain = "www.youdomain.com"

func saveWebCredentials(username: String, password: String, completion: Bool -> Void) {
    SecAddSharedWebCredential(domain, username, password) { error in
        guard error == nil else { print("error saving credentials: \(error)"); return completion(false) }
        completion(true)
    }
}

func getExistingWebCredentials(completion: ((String, String)?, error: String?) -> Void) {
    SecRequestSharedWebCredential(domain, nil) { credentials, error in
        // make sure we got the credentials array back
        guard let credentials = credentials else { return completion(nil, error: String(CFErrorCopyDescription(error))) }

        // make sure there is at least one credential
        let count = CFArrayGetCount(credentials)
        guard count > 0 else { return completion(nil, error: "no credentials stored") }

        // extract the username and password from the credentials dict
        let credentialDict = unsafeBitCast(CFArrayGetValueAtIndex(credentials, 0), CFDictionaryRef.self)
        let username = CFDictionaryGetValue(credentialDict, unsafeBitCast(kSecAttrAccount, UnsafePointer.self))
        let password = CFDictionaryGetValue(credentialDict, unsafeBitCast(kSecSharedPassword, UnsafePointer.self))

        // return via completion block
        completion((String(unsafeBitCast(username, CFStringRef.self)), String(unsafeBitCast(password, CFStringRef.self))), error: nil)
    }
}

这样使用:

// save the credentials
saveWebCredentials("hello", password: "world", completion: { success in

    // retrieve the credentials
    getExistingWebCredentials { credentials, error in
        guard let credentials = credentials else { print("Error: \(error)"); return }
        print("got username: \(credentials.0) password: \(credentials.1)")
    }
})

更新

建议切换到使用 WKWebView,以便您可以轻松提取响应标头。这是样板代码:

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let webView = WKWebView(frame: self.view.bounds)
        webView.navigationDelegate = self
        self.view.addSubview(webView)

        webView.loadRequest(NSURLRequest(URL: NSURL(string: "https://www.google.com")!))
    }

    func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) {
        // make sure the response is a NSHTTPURLResponse
        guard let response = navigationResponse.response as? NSHTTPURLResponse else { return decisionHandler(.Allow) }

        // get the response headers
        let headers = response.allHeaderFields
        print("got headers: \(headers)")

        // allow the request to continue
        decisionHandler(.Allow);
    }
}

【讨论】:

  • 看起来相当不错的解决方案。虽然从我的网络应用程序成功登录后我将如何触发它?
  • 您会将用户名/密码保存在内存中,直到您获得成功的登录响应。根据您登录的内容,将决定您如何检测“成功”。理想情况下你会得到 JSON 或 XML,否则你可能需要 HTML 抓取它
  • 不过就是这样,我不确定如何获得“登录响应”。我的 Web 应用程序通过 AngularJs 的 $http 函数将用户名和密码发送到我的远程 API,并返回成功或不成功的 JSON 响应。如果成功,它会重定向到应用程序,如果不成功,它会显示登录错误。
  • 听起来您应该拦截该重定向。在webView(webView: shouldStartLoadWithRequest: navigationType:) 里面可以得到网址
  • 再次,不确定您要达到的目标。但我假设您会不断检查标题,直到“app-authenticated”设置为true。收到后,您将保存用户名/密码,然后将其从内存中删除
【解决方案2】:

您的代码无法正常工作,因为您没有将 savedUsername 用引号括起来。

你应该有这个:

let loadUsernameJS = "document.getElementById(\"mainLoginUsername\").value = \"\(savedUsername)\";"

另外,this library might help you

【讨论】:

    【解决方案3】:

    您没有将字符串传递给 JavaScript,您应该将变量封装在额外的引号中

    let loadUsernameJS = "document.getElementById(\"mainLoginUsername\").value = \"" + savedUsername + "\";"
    

    let loadUsernameJS = "document.getElementById('mainLoginUsername').value = '" + savedUsername + "';"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-03
      • 2021-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多