AuthWebViewController.swift 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #if os(iOS)
  15. import Foundation
  16. import UIKit
  17. import WebKit
  18. /** @protocol AuthWebViewControllerDelegate
  19. @brief Defines a delegate for AuthWebViewController
  20. */
  21. protocol AuthWebViewControllerDelegate: AnyObject {
  22. /** @fn webViewControllerDidCancel:
  23. @brief Notifies the delegate that the web view controller is being cancelled by the user.
  24. @param webViewController The web view controller in question.
  25. */
  26. func webViewControllerDidCancel(_ controller: AuthWebViewController)
  27. /** @fn webViewController:canHandleURL:
  28. @brief Determines if a URL should be handled by the delegate.
  29. @param URL The URL to handle.
  30. @return Whether the URL could be handled or not.
  31. */
  32. func webViewController(_ controller: AuthWebViewController, canHandle url: URL) -> Bool
  33. /** @fn webViewController:didFailWithError:
  34. @brief Notifies the delegate that the web view controller failed to load a page.
  35. @param webViewController The web view controller in question.
  36. @param error The error that has occurred.
  37. */
  38. func webViewController(_ controller: AuthWebViewController, didFailWithError error: Error)
  39. /** @fn presentURL:UIDelegate:callbackMatcher:completion:
  40. @brief Presents an URL to interact with user.
  41. @param url The URL to present.
  42. @param uiDelegate The UI delegate to present view controller.
  43. @param completion A block to be called either synchronously if the presentation fails to start,
  44. or asynchronously in future on an unspecified thread once the presentation finishes.
  45. */
  46. func present(_ url: URL,
  47. uiDelegate: AuthUIDelegate?,
  48. callbackMatcher: @escaping (URL?) -> Bool,
  49. completion: @escaping (URL?, Error?) -> Void)
  50. }
  51. class AuthWebViewController: UIViewController,
  52. WKNavigationDelegate {
  53. // MARK: - Properties
  54. private var url: URL
  55. weak var delegate: AuthWebViewControllerDelegate?
  56. private weak var webView: AuthWebView?
  57. // MARK: - Initialization
  58. init(url: URL, delegate: AuthWebViewControllerDelegate) {
  59. self.url = url
  60. self.delegate = delegate
  61. super.init(nibName: nil, bundle: nil)
  62. }
  63. @available(*, unavailable)
  64. required init?(coder aDecoder: NSCoder) {
  65. fatalError("init(coder:) has not been implemented")
  66. }
  67. // MARK: - View Lifecycle
  68. override public func loadView() {
  69. let webView = AuthWebView(frame: UIScreen.main.bounds)
  70. webView.webView.navigationDelegate = self
  71. view = webView
  72. self.webView = webView
  73. navigationItem.leftBarButtonItem = UIBarButtonItem(
  74. barButtonSystemItem: .cancel,
  75. target: self,
  76. action: #selector(cancel)
  77. )
  78. }
  79. override public func viewDidAppear(_ animated: Bool) {
  80. super.viewDidAppear(animated)
  81. webView?.webView.load(URLRequest(url: url))
  82. }
  83. // MARK: - Actions
  84. @objc private func cancel() {
  85. delegate?.webViewControllerDidCancel(self)
  86. }
  87. // MARK: - WKNavigationDelegate
  88. public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
  89. decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
  90. let canHandleURL = delegate?.webViewController(
  91. self,
  92. canHandle: navigationAction.request.url ?? url
  93. ) ?? false
  94. if canHandleURL {
  95. decisionHandler(.allow)
  96. } else {
  97. decisionHandler(.cancel)
  98. }
  99. }
  100. public func webView(_ webView: WKWebView,
  101. didStartProvisionalNavigation navigation: WKNavigation!) {
  102. self.webView?.spinner.isHidden = false
  103. self.webView?.spinner.startAnimating()
  104. }
  105. public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
  106. self.webView?.spinner.isHidden = true
  107. self.webView?.spinner.stopAnimating()
  108. }
  109. public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!,
  110. withError error: Error) {
  111. if (error as NSError).domain == NSURLErrorDomain,
  112. (error as NSError).code == NSURLErrorCancelled {
  113. // It's okay for the page to be redirected before it is completely loaded. See b/32028062 .
  114. return
  115. }
  116. // Forward notification to our delegate.
  117. self.webView(webView, didFinish: navigation)
  118. delegate?.webViewController(self, didFailWithError: error)
  119. }
  120. }
  121. #endif