简体   繁体   English

WKWebView委托不响应委托方法调用

[英]WKWebView delegate not responding to delegate method call

I have a WKWebView that is used to present the login screen of my OAuth Identity provider. 我有一个WKWebView,用于显示OAuth身份提供程序的登录屏幕。

import UIKit
import WebKit

protocol OAuth2WKWebViewDelegate: class {
    func didReceiveAuthorizationCode(_ code: String) -> Void
    func didRevokeSession() -> Void
}

class OAuth2WKWebViewController: UIViewController {
    let targetUrl: URLComponents
    let webView = WKWebView()

    weak var delegate: OAuth2WKWebViewDelegate?

    init(targetUrl: URLComponents) {
        self.targetUrl = targetUrl
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        loadUrl()
    }
}

extension OAuth2WKWebViewController: WKNavigationDelegate {
    func loadUrl() {
        guard let url = targetUrl.url else { return }

        view = webView
        webView.load(URLRequest(url: url))
        webView.allowsBackForwardNavigationGestures = true
        webView.navigationDelegate = self
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if let url = navigationAction.request.url {
            if url.scheme == "appdev", url.absoluteString.range(of: "code") != nil {
                let urlParts = url.absoluteString.components(separatedBy: "?")
                let code = urlParts[1].components(separatedBy: "code=")[1]
                delegate?.didReceiveAuthorizationCode(code)
            }

            if url.absoluteString == "appdev://oauth-callback-after-sign-out" {
                delegate?.didRevokeSession()
            }
        }
        decisionHandler(.allow)
    }
}

I also have an IdentityService I use to present this view and respond to it's success / error. 我也有一个IdentityService,用于呈现此视图并响应其成功/错误。

protocol IdentityServiceProtocol {
    var hasValidToken: Bool { get }
    func initAuthCodeFlow() -> Void
    func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
    func storeOAuthTokens(accessToken: String, refreshToken: String, completion: @escaping () -> Void) -> Void
    func renderAuthView() -> Void
}

class IdentityService: IdentityServiceProtocol {

    fileprivate var apiClient: APIClient
    fileprivate var keyChainService: KeyChainService

    init(apiClient: APIClient = APIClient(), keyChainService: KeyChainService = KeyChainService()) {
        self.apiClient = apiClient
        self.keyChainService = keyChainService
    }

    var hasValidToken: Bool {
        return keyChainService.fetchSingleObject(withKey: "AccessToken") != nil
    }

    func initAuthCodeFlow() -> Void {
        let queryItems = ["response_type": "code", "client_id": clientId, "redirect_uri": redirectUri, "state": state, "scope": scope]
        renderOAuthWebView(forService: .auth(company: "benefex"), queryitems: queryItems)
    }

    func initRevokeSession() -> Void {
        guard let refreshToken = keyChainService.fetchSingleObject(withKey: "RefreshToken") else { return }
        let queryItems = ["refresh_token": refreshToken]
        renderOAuthWebView(forService: .revokeSession(company: "benefex"), queryitems: queryItems)
    }

    func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {
        guard let targetUrl = constructURLComponents(endPoint: service, queryItems: queryitems) else { return }
        let webView = OAuth2WKWebViewController(targetUrl: targetUrl)
        webView.delegate = self
        UIApplication.shared.windows.first?.rootViewController = webView
    }

    func storeOAuthTokens(accessToken: String, refreshToken: String, completion: @escaping ()-> Void) -> Void {
        let success = keyChainService.storeManyObjects(["AccessToken": accessToken, "RefreshToken": refreshToken])
        guard success == true else { return }
        completion()
    }

    func renderAuthView() -> Void {
        UIApplication.shared.windows.first?.rootViewController = UINavigationController.init(rootViewController: AuthenticatedViewController())
    }
}

extension IdentityService: OAuth2WKWebViewDelegate {
    func didReceiveAuthorizationCode(_ code: String) {
        apiClient.call(endpoint: IdentityEndpoint.accessToken(company: "benefex", code: code)) { [weak self] (response: OAuthTokenResponse) in
            switch response {
            case .success(let payload):
                guard let accessToken = payload.accessToken, let refreshToken = payload.refreshToken else { return }
                self?.storeOAuthTokens(accessToken: accessToken, refreshToken: refreshToken) { self?.renderAuthView() }
            case .error:
                // login failed for some reason
                print("could not complete request for access token")
            }
        }
    }

    func didRevokeSession() {
        print("This was called")
    }
}

extension IdentityService {
    fileprivate var state: String {
        return generateState(withLength: 20)
    }

    fileprivate func constructURLComponents(endPoint: IdentityEndpoint, queryItems: [String: String]) -> URLComponents? {
        var url = URLComponents(url: endPoint.baseUrl, resolvingAgainstBaseURL: false)
        url?.path = endPoint.path
        url?.queryItems = queryItems.map { URLQueryItem(name: $0.key, value: $0.value) }

        return url
    }

    fileprivate func generateState(withLength len: Int) -> String {
        let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        let length = UInt32(letters.count)

        var randomString = ""
        for _ in 0..<len {
            let rand = arc4random_uniform(length)
            let idx = letters.index(letters.startIndex, offsetBy: Int(rand))
            let letter = letters[idx]
            randomString += String(letter)
        }
        return randomString
    }
}

extension IdentityService {
    var clientId: String {
        let envVar = ProcessInfo.processInfo.environment
        guard let value = envVar["APP_CLIENT_ID"] else { fatalError("Missing APP_CLIENT_ID enviroment variable") }
        return value
    }

    var redirectUri: String {
        let envVar = ProcessInfo.processInfo.environment
        guard let value = envVar["APP_REDIRECT_URI"] else { fatalError("Missing APP_REDIRECT_URI enviroment variable") }
        return value
    }

    var scope: String {
        let envVar = ProcessInfo.processInfo.environment
        guard let value = envVar["APP_SCOPES"] else { fatalError("Missing APP_SCOPES enviroment variable") }
        return value
    }
}

The delegate?.didReceiveAuthorizationCode(code) is working. delegate?.didReceiveAuthorizationCode(code)正在工作。

However when delegate?.didRevokeSession() is called from the WebView, the identity service does not respond. 但是,当从WebView调用delegate?.didRevokeSession() ,身份服务不会响应。

I added some console logs and can see my IdentityService is being de - init when I invoke the logout method. 我添加了一些控制台日志,并且在调用logout方法时可以看到我的IdentityService正在初始化。

I believe this is causing it to do nothing when the delegate method fires. 我相信这导致它在委托方法触发时不执行任何操作。

How can I ensure the delegate method is called still? 如何确保委托方法仍被调用?

This came up when I was searching for answers to my issue - if you are using the 10.2 or 10.2.1 compiler - an issue was occurring for us when compiling in Release (instead of Debug) - where the delegate functions are not being called 这是在我寻找问题的答案时出现的-如果您使用的是10.2或10.2.1编译器-在Release(而不是Debug)中进行编译时,我们遇到的问题-未调用委托函数

The fix for us was to include @objc before all delegate function calls, IE 我们的解决方法是在所有委托函数调用IE之前包含@objc

@objc func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)

If you are in the same scenario - this should help 如果您处于同一情况下-这应该有所帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM