AuthWebViewController.swift 4.8 KB

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