// // ExtensionViewController.swift // passExtension // // Created by Yishi Lin on 13/6/17. // Copyright © 2017 Bob Sun. All rights reserved. // import Foundation import MobileCoreServices import passKit class ExtensionViewController: UIViewController { private lazy var passcodelock: PasscodeExtensionDisplay = { [unowned self] in PasscodeExtensionDisplay(extensionContext: extensionContext!) }() private lazy var passwordsViewController: PasswordsViewController = (children.first as! UINavigationController).viewControllers.first as! PasswordsViewController private lazy var credentialProvider: CredentialProvider = { [unowned self] in CredentialProvider(viewController: self, extensionContext: extensionContext!, afterDecryption: NotificationCenterDispatcher.showOTPNotification) }() private lazy var passwordsTableEntries = PasswordStore.shared.fetchPasswordEntityCoreData(withDir: false) .map(PasswordTableEntry.init) enum Action { case findLogin, fillBrowser, unknown } private var action = Action.unknown override func viewDidLoad() { super.viewDidLoad() view.isHidden = true passwordsViewController.dataSource = PasswordsTableDataSource(entries: passwordsTableEntries) passwordsViewController.selectionDelegate = self passwordsViewController.navigationItem.leftBarButtonItem = UIBarButtonItem( barButtonSystemItem: .cancel, target: self, action: #selector(cancel) ) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) prepareCredentialList() passcodelock.presentPasscodeLockIfNeeded(self, after: { [unowned self] in view.isHidden = false }) } @objc private func cancel(_: AnyObject?) { extensionContext?.completeRequest(returningItems: nil) } private func prepareCredentialList() { guard let attachments = extensionContext?.attachments else { return } func completeTask(_ text: String?) { DispatchQueue.main.async { self.passwordsViewController.showPasswordsWithSuggestion(matching: text ?? "") self.passwordsViewController.navigationItem.prompt = text } } DispatchQueue.global(qos: .userInitiated).async { for attachment in attachments { if attachment.hasURL { self.action = .fillBrowser attachment.extractSearchText { completeTask($0) } } else if attachment.hasFindLoginAction { self.action = .findLogin attachment.extractSearchText { completeTask($0) } } else if attachment.hasPropertyList { self.action = .fillBrowser attachment.extractSearchText { completeTask($0) } } else { self.action = .unknown } } } } } extension ExtensionViewController: PasswordSelectionDelegate { func selected(password: PasswordTableEntry) { switch action { case .findLogin: credentialProvider.provideCredentialsFindLogin(with: password.passwordEntity.path) case .fillBrowser: credentialProvider.provideCredentialsBrowser(with: password.passwordEntity.path) default: extensionContext?.completeRequest(returningItems: nil, completionHandler: nil) } } } extension NSDictionary { func extractSearchText() -> String? { if let value = self[PassExtensionKey.URLStringKey] as? String { if let host = URL(string: value)?.host { return host } return value } if let value = self[NSExtensionJavaScriptPreprocessingResultsKey] as? String { if let host = URL(string: value)?.host { return host } return value } return nil } } extension NSItemProvider { var hasFindLoginAction: Bool { hasItemConformingToTypeIdentifier(PassExtensionActions.findLogin) } var hasURL: Bool { hasItemConformingToTypeIdentifier(kUTTypeURL as String) && registeredTypeIdentifiers.count == 1 } var hasPropertyList: Bool { hasItemConformingToTypeIdentifier(kUTTypePropertyList as String) } } extension NSExtensionContext { /// Get all the attachments to this post. var attachments: [NSItemProvider] { guard let items = inputItems as? [NSExtensionItem] else { return [] } return items.flatMap { $0.attachments ?? [] } } } extension NSItemProvider { /// Extracts the URL from the item provider func extractSearchText(completion: @escaping (String?) -> Void) { loadItem(forTypeIdentifier: kUTTypeURL as String) { item, _ in if let url = item as? NSURL { completion(url.host) } else { completion(nil) } } loadItem(forTypeIdentifier: kUTTypePropertyList as String) { item, _ in if let dict = item as? NSDictionary { if let result = dict[NSExtensionJavaScriptPreprocessingResultsKey] as? NSDictionary { completion(result.extractSearchText()) } } } loadItem(forTypeIdentifier: PassExtensionActions.findLogin) { item, _ in if let dict = item as? NSDictionary { let text = dict.extractSearchText() completion(text) } } } }