From 0db88c5f92d5a83ef0e7204cf99f2585294cd770 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sat, 6 Nov 2021 22:43:50 +0800 Subject: [PATCH 1/3] Fixes #3335 When the app is brought to the foreground from an external action (e.g., tapping on the widget, opening from a notification), a notification is posted (with a slight delay). `MasterFeedViewController` and `SettingsViewController` are observers. `MasterFeedViewController` will dismiss any `SFSafariViewController`s that are presented, while `SettingsViewController` will dismiss itself. --- iOS/AppDelegate.swift | 4 ++++ iOS/MasterFeed/MasterFeedViewController.swift | 8 ++++++++ iOS/SceneDelegate.swift | 10 ++++++++++ iOS/Settings/SettingsViewController.swift | 5 +++++ 4 files changed, 27 insertions(+) diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index 659b7f9b0..168b22a8c 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -210,6 +210,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD default: if let sceneDelegate = response.targetScene?.delegate as? SceneDelegate { sceneDelegate.handle(response) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: { + NotificationCenter.default.post(name: .DidLaunchFromExternalAction, object: nil) + }) + } } diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 7a4711d76..17664b632 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -75,6 +75,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(configureContextMenu(_:)), name: .ActiveExtensionPointsDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(didLaunchFromURLContext), name: .DidLaunchFromExternalAction, object: nil) refreshControl = UIRefreshControl() refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged) @@ -687,6 +688,13 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { present(vc, animated: true) } } + + @objc func didLaunchFromURLContext() { + guard let presentedController = presentedViewController as? SFSafariViewController else { + return + } + presentedController.dismiss(animated: true, completion: nil) + } } // MARK: UIContextMenuInteractionDelegate diff --git a/iOS/SceneDelegate.swift b/iOS/SceneDelegate.swift index 3da542328..a2ac5f862 100644 --- a/iOS/SceneDelegate.swift +++ b/iOS/SceneDelegate.swift @@ -11,6 +11,10 @@ import UserNotifications import Account import Zip +public extension Notification.Name { + static let DidLaunchFromExternalAction = Notification.Name("DidLaunchFromExternalAction") +} + class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? @@ -105,6 +109,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let context = urlContexts.first else { return } DispatchQueue.main.async { + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + NotificationCenter.default.post(name: .DidLaunchFromExternalAction, object: nil) + } + let urlString = context.url.absoluteString // Handle the feed: and feeds: schemes @@ -202,6 +211,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { return } + } } } diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 0d39bbe15..1daf9adfe 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -38,6 +38,7 @@ class SettingsViewController: UITableViewController { NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(activeExtensionPointsDidChange), name: .ActiveExtensionPointsDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(didLaunchFromURLContext), name: .DidLaunchFromExternalAction, object: nil) tableView.register(UINib(nibName: "SettingsComboTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsComboTableViewCell") @@ -389,6 +390,10 @@ class SettingsViewController: UITableViewController { tableView.reloadData() } + @objc func didLaunchFromURLContext() { + dismiss(animated: true, completion: nil) + } + } // MARK: OPML Document Picker From 0469d81c6278ca0eba36953690a05280ba29fbd5 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sat, 6 Nov 2021 22:46:26 +0800 Subject: [PATCH 2/3] consistent naming --- iOS/MasterFeed/MasterFeedViewController.swift | 4 ++-- iOS/Settings/SettingsViewController.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 17664b632..70da39c08 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -75,7 +75,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(configureContextMenu(_:)), name: .ActiveExtensionPointsDidChange, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(didLaunchFromURLContext), name: .DidLaunchFromExternalAction, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(didLaunchFromExternalAction), name: .DidLaunchFromExternalAction, object: nil) refreshControl = UIRefreshControl() refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged) @@ -689,7 +689,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { } } - @objc func didLaunchFromURLContext() { + @objc func didLaunchFromExternalAction() { guard let presentedController = presentedViewController as? SFSafariViewController else { return } diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 1daf9adfe..eeed8472a 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -38,7 +38,7 @@ class SettingsViewController: UITableViewController { NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(activeExtensionPointsDidChange), name: .ActiveExtensionPointsDidChange, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(didLaunchFromURLContext), name: .DidLaunchFromExternalAction, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(didLaunchFromExternalAction), name: .DidLaunchFromExternalAction, object: nil) tableView.register(UINib(nibName: "SettingsComboTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsComboTableViewCell") @@ -390,7 +390,7 @@ class SettingsViewController: UITableViewController { tableView.reloadData() } - @objc func didLaunchFromURLContext() { + @objc func didLaunchFromExternalAction() { dismiss(animated: true, completion: nil) } From 8b39dc4abb6de52beace03c69c74fcb3aff37ee6 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Mon, 8 Nov 2021 09:52:12 +0800 Subject: [PATCH 3/3] Uses SceneCoordinator Adds a `func` to SceneCoordinator to handle dismisses vis-a-vis using notifications. --- iOS/AppDelegate.swift | 3 +-- iOS/MasterFeed/MasterFeedViewController.swift | 9 +-------- iOS/SceneCoordinator.swift | 17 +++++++++++++++++ iOS/SceneDelegate.swift | 6 +----- iOS/Settings/SettingsViewController.swift | 5 ----- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index 168b22a8c..02d0bf1ad 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -211,9 +211,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if let sceneDelegate = response.targetScene?.delegate as? SceneDelegate { sceneDelegate.handle(response) DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: { - NotificationCenter.default.post(name: .DidLaunchFromExternalAction, object: nil) + sceneDelegate.coordinator.dismissIfLaunchingFromExternalAction() }) - } } diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 70da39c08..f988830a9 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -75,7 +75,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(configureContextMenu(_:)), name: .ActiveExtensionPointsDidChange, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(didLaunchFromExternalAction), name: .DidLaunchFromExternalAction, object: nil) refreshControl = UIRefreshControl() refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged) @@ -688,13 +687,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { present(vc, animated: true) } } - - @objc func didLaunchFromExternalAction() { - guard let presentedController = presentedViewController as? SFSafariViewController else { - return - } - presentedController.dismiss(animated: true, completion: nil) - } + } // MARK: UIContextMenuInteractionDelegate diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index c03018039..1c6a526b9 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -1327,6 +1327,23 @@ class SceneCoordinator: NSObject, UndoableCommandRunner { } + /// This will dismiss the foremost view controller if the user + /// has launched from an external action (i.e., a widget tap, or + /// selecting an artice via a notification). + /// + /// The dismiss is only applicable if the view controller is a + /// `SFSafariViewController` or `SettingsViewController`, + /// otherwise, this function does nothing. + func dismissIfLaunchingFromExternalAction() { + guard let presentedController = masterFeedViewController.presentedViewController else { return } + + if presentedController.isKind(of: SFSafariViewController.self) { + presentedController.dismiss(animated: true, completion: nil) + } + guard let settings = presentedController.children.first as? SettingsViewController else { return } + settings.dismiss(animated: true, completion: nil) + } + } // MARK: UISplitViewControllerDelegate diff --git a/iOS/SceneDelegate.swift b/iOS/SceneDelegate.swift index a2ac5f862..523446eed 100644 --- a/iOS/SceneDelegate.swift +++ b/iOS/SceneDelegate.swift @@ -11,10 +11,6 @@ import UserNotifications import Account import Zip -public extension Notification.Name { - static let DidLaunchFromExternalAction = Notification.Name("DidLaunchFromExternalAction") -} - class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? @@ -111,7 +107,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { DispatchQueue.main.async { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - NotificationCenter.default.post(name: .DidLaunchFromExternalAction, object: nil) + self.coordinator.dismissIfLaunchingFromExternalAction() } let urlString = context.url.absoluteString diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index eeed8472a..0d39bbe15 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -38,7 +38,6 @@ class SettingsViewController: UITableViewController { NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(activeExtensionPointsDidChange), name: .ActiveExtensionPointsDidChange, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(didLaunchFromExternalAction), name: .DidLaunchFromExternalAction, object: nil) tableView.register(UINib(nibName: "SettingsComboTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsComboTableViewCell") @@ -390,10 +389,6 @@ class SettingsViewController: UITableViewController { tableView.reloadData() } - @objc func didLaunchFromExternalAction() { - dismiss(animated: true, completion: nil) - } - } // MARK: OPML Document Picker