AuthWebViewController.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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. /// Defines a delegate for AuthWebViewController
  19. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  20. protocol AuthWebViewControllerDelegate: AnyObject {
  21. /// Notifies the delegate that the web view controller is being cancelled by the user.
  22. /// - Parameter webViewController: The web view controller in question.
  23. func webViewControllerDidCancel(_ controller: AuthWebViewController)
  24. /// Determines if a URL should be handled by the delegate.
  25. /// - Parameter url: The URL to handle.
  26. /// - Returns: Whether the URL could be handled or not.
  27. func webViewController(_ controller: AuthWebViewController, canHandle url: URL) -> Bool
  28. /// Notifies the delegate that the web view controller failed to load a page.
  29. /// - Parameter webViewController: The web view controller in question.
  30. /// - Parameter error: The error that has occurred.
  31. func webViewController(_ controller: AuthWebViewController, didFailWithError error: Error)
  32. /// Presents an URL to interact with user.
  33. /// - Parameter url: The URL to present.
  34. /// - Parameter uiDelegate: The UI delegate to present view controller.
  35. /// - Parameter completion: A block to be called either synchronously if the presentation fails
  36. /// to start, or asynchronously in future on an unspecified thread once the presentation
  37. /// finishes.
  38. func present(_ url: URL,
  39. uiDelegate: AuthUIDelegate?,
  40. callbackMatcher: @escaping (URL?) -> Bool,
  41. completion: @escaping (URL?, Error?) -> Void)
  42. }
  43. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  44. class AuthWebViewController: UIViewController,
  45. WKNavigationDelegate {
  46. // MARK: - Properties
  47. private var url: URL
  48. weak var delegate: AuthWebViewControllerDelegate?
  49. private weak var webView: AuthWebView?
  50. // MARK: - Initialization
  51. init(url: URL, delegate: AuthWebViewControllerDelegate) {
  52. self.url = url
  53. self.delegate = delegate
  54. super.init(nibName: nil, bundle: nil)
  55. }
  56. @available(*, unavailable)
  57. required init?(coder aDecoder: NSCoder) {
  58. fatalError("init(coder:) has not been implemented")
  59. }
  60. // MARK: - View Lifecycle
  61. override func loadView() {
  62. let webView = AuthWebView(frame: UIScreen.main.bounds)
  63. webView.webView.navigationDelegate = self
  64. view = webView
  65. self.webView = webView
  66. navigationItem.leftBarButtonItem = UIBarButtonItem(
  67. barButtonSystemItem: .cancel,
  68. target: self,
  69. action: #selector(cancel)
  70. )
  71. }
  72. override func viewDidAppear(_ animated: Bool) {
  73. super.viewDidAppear(animated)
  74. webView?.webView.load(URLRequest(url: url))
  75. }
  76. // MARK: - Actions
  77. @objc private func cancel() {
  78. delegate?.webViewControllerDidCancel(self)
  79. }
  80. // MARK: - WKNavigationDelegate
  81. func webView(_ webView: WKWebView,
  82. decidePolicyFor navigationAction: WKNavigationAction) async
  83. -> WKNavigationActionPolicy {
  84. _ = delegate?.webViewController(
  85. self,
  86. canHandle: navigationAction.request.url ?? url
  87. )
  88. return .allow
  89. }
  90. func webView(_ webView: WKWebView,
  91. didStartProvisionalNavigation navigation: WKNavigation!) {
  92. self.webView?.spinner.isHidden = false
  93. self.webView?.spinner.startAnimating()
  94. }
  95. func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
  96. self.webView?.spinner.isHidden = true
  97. self.webView?.spinner.stopAnimating()
  98. }
  99. func webView(_ webView: WKWebView, didFail navigation: WKNavigation!,
  100. withError error: Error) {
  101. if (error as NSError).domain == NSURLErrorDomain,
  102. (error as NSError).code == NSURLErrorCancelled {
  103. // It's okay for the page to be redirected before it is completely loaded. See b/32028062 .
  104. return
  105. }
  106. // Forward notification to our delegate.
  107. self.webView(webView, didFinish: navigation)
  108. delegate?.webViewController(self, didFailWithError: error)
  109. }
  110. }
  111. #endif