ios/BitwardenActionExtension/ActionViewController.swift
2025-10-06 15:18:35 -05:00

124 lines
3.7 KiB
Swift

import BitwardenKit
import BitwardenShared
import MobileCoreServices
import UIKit
import UniformTypeIdentifiers
class ActionViewController: UIViewController {
// MARK: Properties
/// The app's theme.
var appTheme: AppTheme = .default
/// The processor that manages application level logic.
private var appProcessor: AppProcessor?
/// A helper class for processing the input items from the extension.
private let actionExtensionHelper = ActionExtensionHelper()
// MARK: View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
guard let inputItems = extensionContext?.inputItems as? [NSExtensionItem] else { return }
actionExtensionHelper.processInputItems(inputItems)
initializeApp()
}
// MARK: Private
/// Completes the extension request with a dictionary of item data to return to the host app.
///
/// - Parameter itemData: The dictionary of item data to return to the host app from the extension.
///
private func completeRequest(itemData: [String: Any]) {
let resultsProvider = NSItemProvider(
item: itemData as NSSecureCoding,
typeIdentifier: UTType.propertyList.identifier,
)
let resultsItem = NSExtensionItem()
resultsItem.attachments = [resultsProvider]
extensionContext?.completeRequest(returningItems: [resultsItem])
}
/// Sets up and initializes the app and UI.
///
private func initializeApp() {
let errorReporter = OSLogErrorReporter()
let services = ServiceContainer(appContext: .appExtension, errorReporter: errorReporter)
let appModule = DefaultAppModule(appExtensionDelegate: self, services: services)
let appProcessor = AppProcessor(appModule: appModule, services: services)
self.appProcessor = appProcessor
let initialRoute: AppRoute? = if actionExtensionHelper.isAppExtensionSetup {
AppRoute.extensionSetup(.extensionActivation(type: .appExtension))
} else {
nil
}
Task {
await appProcessor.start(
appContext: .appExtension,
initialRoute: initialRoute,
navigator: self,
window: nil,
)
}
}
}
// MARK: - AppExtensionDelegate
extension ActionViewController: AppExtensionDelegate {
var authCompletionRoute: AppRoute? {
actionExtensionHelper.authCompletionRoute
}
var canAutofill: Bool {
actionExtensionHelper.canAutofill
}
var isInAppExtension: Bool { true }
var isInAppExtensionSaveLoginFlow: Bool {
actionExtensionHelper.isProviderSaveLogin
}
var uri: String? { actionExtensionHelper.uri }
func completeAutofillRequest(username: String, password: String, fields: [(String, String)]?) {
let itemData = actionExtensionHelper.itemDataToCompleteRequest(
username: username,
password: password,
fields: fields ?? [],
)
completeRequest(itemData: itemData)
}
func didCancel() {
extensionContext?.completeRequest(returningItems: nil)
}
}
// MARK: - RootNavigator
extension ActionViewController: RootNavigator {
var rootViewController: UIViewController? { self }
func show(child: Navigator) {
if let fromViewController = children.first {
fromViewController.willMove(toParent: nil)
fromViewController.view.removeFromSuperview()
fromViewController.removeFromParent()
}
if let toViewController = child.rootViewController {
addChild(toViewController)
view.addConstrained(subview: toViewController.view)
toViewController.didMove(toParent: self)
}
}
}