Files
OAuthSwift/Sources/Handler/OAuthWebViewController.swift
2024-04-16 14:21:34 +02:00

197 lines
8.3 KiB
Swift

//
// OAuthWebViewController.swift
// OAuthSwift
//
// Created by Dongri Jin on 2/11/15.
// Copyright (c) 2015 Dongri Jin. All rights reserved.
//
import Foundation
#if os(iOS) || os(visionOS) || os(tvOS)
import UIKit
public typealias OAuthViewController = UIViewController
#elseif os(watchOS)
import WatchKit
public typealias OAuthViewController = WKInterfaceController
#elseif os(OSX)
import AppKit
public typealias OAuthViewController = NSViewController
#endif
/// Delegate for OAuthWebViewController
public protocol OAuthWebViewControllerDelegate: AnyObject {
#if os(iOS) || os(visionOS) || os(tvOS)
/// Did web view presented (work only without navigation controller)
func oauthWebViewControllerDidPresent()
/// Did web view dismiss (work only without navigation controller)
func oauthWebViewControllerDidDismiss()
#endif
func oauthWebViewControllerWillAppear()
func oauthWebViewControllerDidAppear()
func oauthWebViewControllerWillDisappear()
func oauthWebViewControllerDidDisappear()
}
/// A web view controller, which handler OAuthSwift authentification. Must be override to display a web view.
open class OAuthWebViewController: OAuthViewController, OAuthSwiftURLHandlerType {
#if os(iOS) || os(visionOS) || os(tvOS) || os(OSX)
/// Delegate for this view
public weak var delegate: OAuthWebViewControllerDelegate?
#endif
#if os(iOS) || os(visionOS) || os(tvOS)
/// If controller have an navigation controller, application top view controller could be used if true
public var useTopViewControlerInsteadOfNavigation = false
/// Set false to disable present animation.
public var presentViewControllerAnimated = true
/// Set false to disable dismiss animation.
public var dismissViewControllerAnimated = true
public var topViewController: UIViewController? {
#if !OAUTH_APP_EXTENSIONS
return UIApplication.topViewController
#else
return nil
#endif
}
#elseif os(OSX)
/// How to present this view controller if parent view controller set
public enum Present {
case asModalWindow
case asSheet
case asPopover(relativeToRect: NSRect, ofView: NSView, preferredEdge: NSRectEdge, behavior: NSPopover.Behavior)
case transitionFrom(fromViewController: NSViewController, options: NSViewController.TransitionOptions)
case animator(animator: NSViewControllerPresentationAnimator)
case segue(segueIdentifier: String)
}
public var present: Present = .asModalWindow
#endif
open func handle(_ url: URL) {
// do UI in main thread
OAuthSwift.main { [unowned self] in
self.doHandle(url)
}
}
#if os(watchOS)
public static var userActivityType: String = "org.github.dongri.oauthswift.connect"
#endif
open func doHandle(_ url: URL) {
OAuthSwift.log?.trace("OAuthWebViewController: present Safari view controller, url: \(url)")
#if os(iOS) || os(visionOS) || os(tvOS)
let completion: () -> Void = { [unowned self] in
self.delegate?.oauthWebViewControllerDidPresent()
}
if let navigationController = self.navigationController, (!useTopViewControlerInsteadOfNavigation || self.topViewController == nil) {
navigationController.pushViewController(self, animated: presentViewControllerAnimated)
} else if let p = self.parent {
p.present(self, animated: presentViewControllerAnimated, completion: completion)
} else if let topViewController = topViewController {
topViewController.present(self, animated: presentViewControllerAnimated, completion: completion)
} else {
// assert no presentation
assertionFailure("Failed to present. Maybe add a parent")
}
#elseif os(watchOS)
if url.scheme == "http" || url.scheme == "https" {
self.updateUserActivity(OAuthWebViewController.userActivityType, userInfo: nil, webpageURL: url)
}
#elseif os(OSX)
if let p = self.parent { // default behaviour if this controller affected as child controller
switch self.present {
case .asSheet:
p.presentAsSheet(self)
case .asModalWindow:
p.presentAsModalWindow(self)
// FIXME: if we present as window, window close must detected and oauthswift.cancel() must be called...
case .asPopover(let positioningRect, let positioningView, let preferredEdge, let behavior):
p.present(self, asPopoverRelativeTo: positioningRect, of: positioningView, preferredEdge: preferredEdge, behavior: behavior)
case .transitionFrom(let fromViewController, let options):
let completion: () -> Void = { /*[unowned self] in*/
// self.delegate?.oauthWebViewControllerDidPresent()
}
p.transition(from: fromViewController, to: self, options: options, completionHandler: completion)
case .animator(let animator):
p.present(self, animator: animator)
case .segue(let segueIdentifier):
p.performSegue(withIdentifier: segueIdentifier, sender: self) // The segue must display self.view
}
} else if let window = self.view.window {
window.makeKeyAndOrderFront(nil)
} else {
assertionFailure("Failed to present. Add controller into a window or add a parent")
}
// or create an NSWindow or NSWindowController (/!\ keep a strong reference on it)
#endif
}
open func dismissWebViewController() {
OAuthSwift.log?.trace("OAuthWebViewController: dismiss view controller")
#if os(iOS) || os(visionOS) || os(tvOS)
let completion: () -> Void = { [weak self] in
self?.delegate?.oauthWebViewControllerDidDismiss()
}
if let navigationController = self.navigationController, (!useTopViewControlerInsteadOfNavigation || self.topViewController == nil) {
navigationController.popViewController(animated: dismissViewControllerAnimated)
} else if let parentViewController = self.parent {
// The presenting view controller is responsible for dismissing the view controller it presented
parentViewController.dismiss(animated: dismissViewControllerAnimated, completion: completion)
} else if let topViewController = topViewController {
topViewController.dismiss(animated: dismissViewControllerAnimated, completion: completion)
} else {
// keep old code...
self.dismiss(animated: dismissViewControllerAnimated, completion: completion)
}
#elseif os(watchOS)
self.dismiss()
#elseif os(OSX)
if self.presentingViewController != nil {
self.dismiss(nil)
if self.parent != nil {
self.removeFromParent()
}
} else if let window = self.view.window {
window.performClose(nil)
}
#endif
}
// MARK: overrides
#if os(iOS) || os(visionOS) || os(tvOS)
open override func viewWillAppear(_ animated: Bool) {
self.delegate?.oauthWebViewControllerWillAppear()
}
open override func viewDidAppear(_ animated: Bool) {
self.delegate?.oauthWebViewControllerDidAppear()
}
open override func viewWillDisappear(_ animated: Bool) {
self.delegate?.oauthWebViewControllerWillDisappear()
}
open override func viewDidDisappear(_ animated: Bool) {
self.delegate?.oauthWebViewControllerDidDisappear()
}
#elseif os(OSX)
open override func viewWillAppear() {
self.delegate?.oauthWebViewControllerWillAppear()
}
open override func viewDidAppear() {
self.delegate?.oauthWebViewControllerDidAppear()
}
open override func viewWillDisappear() {
self.delegate?.oauthWebViewControllerWillDisappear()
}
open override func viewDidDisappear() {
self.delegate?.oauthWebViewControllerDidDisappear()
}
#endif
}