diff --git a/SignalShareExtension/ShareViewController.swift b/SignalShareExtension/ShareViewController.swift index 3fd41a196..f7de42acf 100644 --- a/SignalShareExtension/ShareViewController.swift +++ b/SignalShareExtension/ShareViewController.swift @@ -18,9 +18,10 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE private var progressPoller: ProgressPoller? var loadViewController: SAELoadViewController? + let shareViewNavigationController: UINavigationController = UINavigationController() + override open func loadView() { super.loadView() - Logger.debug("\(self.logTag) \(#function)") // This should be the first thing we do. @@ -56,7 +57,7 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE // most of the singletons, etc.). We just want to show an error view and // abort. isReadyForAppExtensions = OWSPreferences.isReadyForAppExtensions() - if !isReadyForAppExtensions { + guard isReadyForAppExtensions else { // If we don't have TSSStorageManager, we can't consult TSAccountManager // for isRegistered, so we use OWSPreferences which is usually-accurate // copy of that state. @@ -79,9 +80,7 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE } Logger.debug("\(self.logTag) setup is slow - showing loading screen") - - let navigationController = UINavigationController(rootViewController: loadViewController) - self.present(navigationController, animated: true) + self.showPrimaryViewController(loadViewController) }.retainUntilComplete() // We shouldn't set up our environment until after we've consulted isReadyForAppExtensions. @@ -117,6 +116,7 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE } deinit { + Logger.info("\(self.logTag) dealloc") NotificationCenter.default.removeObserver(self) } @@ -298,7 +298,7 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE private func showErrorView(title: String, message: String) { let viewController = SAEFailedViewController(delegate:self, title:title, message:message) - self.setViewControllers([viewController], animated: false) + self.showPrimaryViewController(viewController) } // MARK: View Lifecycle @@ -363,22 +363,30 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE // MARK: Helpers + // This view controller is not visible to the user. It exists to intercept touches, set up the + // extensions dependencies, and eventually present a visible view to the user. + // For speed of presentation, we only present a single modal, and if it's already been presented + // we swap out the contents. + // e.g. if loading is taking a while, the user will see the load screen presented with a modal + // animation. Next, when loading completes, the load view will be switched out for the contact + // picker view. + private func showPrimaryViewController(_ viewController: UIViewController) { + shareViewNavigationController.setViewControllers([viewController], animated: false) + if self.presentedViewController == nil { + Logger.debug("\(self.logTag) presenting modally: \(viewController)") + self.present(shareViewNavigationController, animated: true) + } else { + Logger.debug("\(self.logTag) modal already presented. swapping modal content for: \(viewController)") + assert(self.presentedViewController == shareViewNavigationController) + } + } + private func presentConversationPicker() { self.buildAttachment().then { attachment -> Void in let conversationPicker = SharingThreadPickerViewController(shareViewDelegate: self) - let navigationController = UINavigationController(rootViewController: conversationPicker) - navigationController.isNavigationBarHidden = true conversationPicker.attachment = attachment - if let presentedViewController = self.presentedViewController { - Logger.debug("\(self.logTag) dismissing \(presentedViewController) before presenting conversation picker") - self.dismiss(animated: true) { - self.present(navigationController, animated: true) - } - } else { - Logger.debug("\(self.logTag) no other modal, presenting conversation picker immediately") - self.present(navigationController, animated: true) - } - + self.shareViewNavigationController.isNavigationBarHidden = true + self.showPrimaryViewController(conversationPicker) Logger.info("showing picker with attachment: \(attachment)") }.catch { error in let alertTitle = NSLocalizedString("SHARE_EXTENSION_UNABLE_TO_BUILD_ATTACHMENT_ALERT_TITLE", comment: "Shown when trying to share content to a Signal user for the share extension. Followed by failure details.")