do not dismiss views when application is resumed (#605)
* do not dismiss views when application is resumed * prevents the PasswordNavigationViewController and PasswordDetailTableViewController from being dismissed when the app is put to the background and then brought back to the foreground * Instead, the PasswordEntities are re-fetched from the context by their path to handle the re-creation of the entities during an update process that could have run in the background * update SwiftLint to version 0.50.* * update SwiftFormat to 0.51.* --------- Co-authored-by: Mingshen Sun <bob@mssun.me>
This commit is contained in:
parent
83c6ae33dc
commit
f2a0c4ccf1
10 changed files with 77 additions and 26 deletions
|
|
@ -44,7 +44,6 @@
|
||||||
numberFormatting, \
|
numberFormatting, \
|
||||||
# opaqueGenericParameters, \
|
# opaqueGenericParameters, \
|
||||||
# organizeDeclarations, \
|
# organizeDeclarations, \
|
||||||
preferDouble, \
|
|
||||||
preferKeyPath, \
|
preferKeyPath, \
|
||||||
redundantBackticks, \
|
redundantBackticks, \
|
||||||
redundantBreak, \
|
redundantBreak, \
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
||||||
alert.addAction(
|
alert.addAction(
|
||||||
UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in
|
UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in
|
||||||
SVProgressHUD.show(withStatus: "Erasing...".localize())
|
SVProgressHUD.show(withStatus: "Erasing...".localize())
|
||||||
self.passwordStore.erase()
|
passwordStore.erase()
|
||||||
self.navigationController!.popViewController(animated: true)
|
navigationController!.popViewController(animated: true)
|
||||||
SVProgressHUD.showSuccess(withStatus: "Done".localize())
|
SVProgressHUD.showSuccess(withStatus: "Done".localize())
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
}
|
}
|
||||||
|
|
@ -67,8 +67,8 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
||||||
UIAlertAction(title: "DiscardAllLocalChanges".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in
|
UIAlertAction(title: "DiscardAllLocalChanges".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in
|
||||||
SVProgressHUD.show(withStatus: "Resetting...".localize())
|
SVProgressHUD.show(withStatus: "Resetting...".localize())
|
||||||
do {
|
do {
|
||||||
let numberDiscarded = try self.passwordStore.reset()
|
let numberDiscarded = try passwordStore.reset()
|
||||||
self.navigationController!.popViewController(animated: true)
|
navigationController!.popViewController(animated: true)
|
||||||
SVProgressHUD.showSuccess(withStatus: "DiscardedCommits(%d)".localize(numberDiscarded))
|
SVProgressHUD.showSuccess(withStatus: "DiscardedCommits(%d)".localize(numberDiscarded))
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,21 @@ import UIKit
|
||||||
import YubiKit
|
import YubiKit
|
||||||
|
|
||||||
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate, AlertPresenting {
|
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate, AlertPresenting {
|
||||||
var passwordEntity: PasswordEntity?
|
var passwordEntity: PasswordEntity? {
|
||||||
|
didSet {
|
||||||
|
passwordPath = passwordEntity?.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var password: Password?
|
private var password: Password?
|
||||||
private var passwordImage: UIImage?
|
private var passwordImage: UIImage?
|
||||||
private var oneTimePasswordIndexPath: IndexPath?
|
private var oneTimePasswordIndexPath: IndexPath?
|
||||||
private var shouldPopCurrentView = false
|
private var shouldPopCurrentView = false
|
||||||
private let passwordStore = PasswordStore.shared
|
private let passwordStore = PasswordStore.shared
|
||||||
|
|
||||||
|
// preserve path so it can be reloaded even if the passwordEntity is deleted during the update process
|
||||||
|
private var passwordPath: String?
|
||||||
|
|
||||||
private lazy var editUIBarButtonItem: UIBarButtonItem = {
|
private lazy var editUIBarButtonItem: UIBarButtonItem = {
|
||||||
let uiBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit))
|
let uiBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(pressEdit))
|
||||||
return uiBarButtonItem
|
return uiBarButtonItem
|
||||||
|
|
@ -74,6 +82,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
|
|
||||||
// reset the data table if the disaply settings have been changed
|
// reset the data table if the disaply settings have been changed
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPasswordSelector), name: .passwordDetailDisplaySettingChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(decryptThenShowPasswordSelector), name: .passwordDetailDisplaySettingChanged, object: nil)
|
||||||
|
|
||||||
|
// A Siri shortcut can change the state of the app in the background. Hence, reload when opening the app.
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(actOnPossiblePasswordStoreUpdate), name: UIApplication.willEnterForegroundNotification, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
|
@ -526,6 +537,23 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PasswordDetailTableViewController {
|
extension PasswordDetailTableViewController {
|
||||||
|
@objc
|
||||||
|
func actOnPossiblePasswordStoreUpdate() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if let path = self.passwordPath {
|
||||||
|
// reload PasswordEntity because all PasswordEntities are re-created on PasswordStore update
|
||||||
|
self.passwordEntity = PasswordStore.shared.fetchPasswordEntity(with: path)
|
||||||
|
|
||||||
|
// dismiss if the PasswordEntity does not exist anymore
|
||||||
|
if self.passwordEntity == nil {
|
||||||
|
self.navigationController?.popToRootViewController(animated: true)
|
||||||
|
} else {
|
||||||
|
self.decryptThenShowPassword()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func requestYubiKeyPIN(completion: @escaping (String) -> Void, cancellation: @escaping () -> Void) {
|
private func requestYubiKeyPIN(completion: @escaping (String) -> Void, cancellation: @escaping () -> Void) {
|
||||||
let alert = UIAlertController(title: "YubiKey PIN", message: "Verify YubiKey OpenPGP PIN.", preferredStyle: .alert)
|
let alert = UIAlertController(title: "YubiKey PIN", message: "Verify YubiKey OpenPGP PIN.", preferredStyle: .alert)
|
||||||
alert.addAction(
|
alert.addAction(
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ class PasswordEditorTableViewController: UITableViewController {
|
||||||
let alert = UIAlertController(title: "DeletePassword?".localize(), message: nil, preferredStyle: UIAlertController.Style.alert)
|
let alert = UIAlertController(title: "DeletePassword?".localize(), message: nil, preferredStyle: UIAlertController.Style.alert)
|
||||||
alert.addAction(
|
alert.addAction(
|
||||||
UIAlertAction(title: "Delete".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in
|
UIAlertAction(title: "Delete".localize(), style: UIAlertAction.Style.destructive) { [unowned self] _ in
|
||||||
self.performSegue(withIdentifier: "deletePasswordSegue", sender: self)
|
performSegue(withIdentifier: "deletePasswordSegue", sender: self)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
alert.addAction(UIAlertAction.cancel())
|
alert.addAction(UIAlertAction.cancel())
|
||||||
|
|
@ -442,9 +442,9 @@ extension PasswordEditorTableViewController: SFSafariViewControllerDelegate {
|
||||||
alert.addAction(
|
alert.addAction(
|
||||||
UIAlertAction(title: "Yes", style: UIAlertAction.Style.default) { [unowned self] _ in
|
UIAlertAction(title: "Yes", style: UIAlertAction.Style.default) { [unowned self] _ in
|
||||||
// update tableData so to make sure reloadData() works correctly
|
// update tableData so to make sure reloadData() works correctly
|
||||||
self.tableData[self.passwordSection][0][PasswordEditorCellKey.content] = generatedPassword
|
tableData[passwordSection][0][PasswordEditorCellKey.content] = generatedPassword
|
||||||
// update cell manually, no need to call reloadData()
|
// update cell manually, no need to call reloadData()
|
||||||
self.fillPasswordCell?.setContent(content: generatedPassword)
|
fillPasswordCell?.setContent(content: generatedPassword)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
alert.addAction(UIAlertAction.cancel())
|
alert.addAction(UIAlertAction.cancel())
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,14 @@ class PasswordNavigationViewController: UIViewController {
|
||||||
@IBOutlet var tableView: UITableView!
|
@IBOutlet var tableView: UITableView!
|
||||||
|
|
||||||
var dataSource: PasswordNavigationDataSource?
|
var dataSource: PasswordNavigationDataSource?
|
||||||
var parentPasswordEntity: PasswordEntity?
|
var parentPasswordEntity: PasswordEntity? {
|
||||||
|
didSet {
|
||||||
|
parentPath = parentPasswordEntity?.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// preserve parent path so it can be reloaded even if the parentPasswordEntity is deleted during the update process
|
||||||
|
private var parentPath: String?
|
||||||
|
|
||||||
var viewingUnsyncedPasswords = false
|
var viewingUnsyncedPasswords = false
|
||||||
var tapTabBarTime: TimeInterval = 0
|
var tapTabBarTime: TimeInterval = 0
|
||||||
|
|
@ -181,13 +188,13 @@ class PasswordNavigationViewController: UIViewController {
|
||||||
private func configureNotification() {
|
private func configureNotification() {
|
||||||
let notificationCenter = NotificationCenter.default
|
let notificationCenter = NotificationCenter.default
|
||||||
// Reset the data table if some password (maybe another one) has been updated.
|
// Reset the data table if some password (maybe another one) has been updated.
|
||||||
notificationCenter.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordStoreUpdated, object: nil)
|
notificationCenter.addObserver(self, selector: #selector(actOnPossiblePasswordStoreUpdate), name: .passwordStoreUpdated, object: nil)
|
||||||
// Reset the data table if the disaply settings have been changed.
|
// Reset the data table if the disaply settings have been changed.
|
||||||
notificationCenter.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordDisplaySettingChanged, object: nil)
|
notificationCenter.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordDisplaySettingChanged, object: nil)
|
||||||
// Search entrypoint for home screen quick action.
|
// Search entrypoint for home screen quick action.
|
||||||
notificationCenter.addObserver(self, selector: #selector(actOnSearchNotification), name: .passwordSearch, object: nil)
|
notificationCenter.addObserver(self, selector: #selector(actOnSearchNotification), name: .passwordSearch, object: nil)
|
||||||
// A Siri shortcut can change the state of the app in the background. Hence, reload when opening the app.
|
// A Siri shortcut can change the state of the app in the background. Hence, reload when opening the app.
|
||||||
notificationCenter.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: UIApplication.willEnterForegroundNotification, object: nil)
|
notificationCenter.addObserver(self, selector: #selector(actOnPossiblePasswordStoreUpdate), name: UIApplication.willEnterForegroundNotification, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
|
|
@ -352,6 +359,23 @@ extension PasswordNavigationViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func actOnPossiblePasswordStoreUpdate() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if let path = self.parentPath {
|
||||||
|
// reload parent because all PasswordEntities are re-created on PasswordStore update
|
||||||
|
self.parentPasswordEntity = PasswordStore.shared.fetchPasswordEntity(with: path)
|
||||||
|
|
||||||
|
// pop to the root controller if the parent does not exist anymore
|
||||||
|
if self.parentPasswordEntity == nil {
|
||||||
|
self.navigationController?.popToRootViewController(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.resetViews()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func resetViews() {
|
func resetViews() {
|
||||||
configureTableView(in: parentPasswordEntity)
|
configureTableView(in: parentPasswordEntity)
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
|
|
@ -447,14 +471,14 @@ extension PasswordNavigationViewController: PasswordAlertPresenter {
|
||||||
}
|
}
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||||
do {
|
do {
|
||||||
let pullOptions = gitCredential.getCredentialOptions(passwordProvider: self.present)
|
let pullOptions = gitCredential.getCredentialOptions(passwordProvider: present)
|
||||||
try PasswordStore.shared.pullRepository(options: pullOptions) { git_transfer_progress, _ in
|
try PasswordStore.shared.pullRepository(options: pullOptions) { git_transfer_progress, _ in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects) / Float(git_transfer_progress.pointee.total_objects), status: "PullingFromRemoteRepository".localize())
|
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects) / Float(git_transfer_progress.pointee.total_objects), status: "PullingFromRemoteRepository".localize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if PasswordStore.shared.numberOfLocalCommits > 0 {
|
if PasswordStore.shared.numberOfLocalCommits > 0 {
|
||||||
let pushOptions = gitCredential.getCredentialOptions(passwordProvider: self.present)
|
let pushOptions = gitCredential.getCredentialOptions(passwordProvider: present)
|
||||||
try PasswordStore.shared.pushRepository(options: pushOptions) { current, total, _, _ in
|
try PasswordStore.shared.pushRepository(options: pushOptions) { current, total, _, _ in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
SVProgressHUD.showProgress(Float(current) / Float(total), status: "PushingToRemoteRepository".localize())
|
SVProgressHUD.showProgress(Float(current) / Float(total), status: "PushingToRemoteRepository".localize())
|
||||||
|
|
|
||||||
|
|
@ -38,13 +38,13 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||||
passcodelock.presentPasscodeLockIfNeeded(self) {
|
passcodelock.presentPasscodeLockIfNeeded(self) {
|
||||||
self.view.isHidden = true
|
self.view.isHidden = true
|
||||||
} after: { [unowned self] in
|
} after: { [unowned self] in
|
||||||
self.view.isHidden = false
|
view.isHidden = false
|
||||||
self.credentialProvider.identifier = serviceIdentifiers.first
|
credentialProvider.identifier = serviceIdentifiers.first
|
||||||
let url = serviceIdentifiers.first
|
let url = serviceIdentifiers.first
|
||||||
.map(\.identifier)
|
.map(\.identifier)
|
||||||
.flatMap(URL.init)
|
.flatMap(URL.init)
|
||||||
self.passwordsViewController.navigationItem.prompt = url?.host
|
passwordsViewController.navigationItem.prompt = url?.host
|
||||||
self.passwordsViewController.showPasswordsWithSuggestion(matching: url?.host ?? "")
|
passwordsViewController.showPasswordsWithSuggestion(matching: url?.host ?? "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||||
passcodelock.presentPasscodeLockIfNeeded(self) {
|
passcodelock.presentPasscodeLockIfNeeded(self) {
|
||||||
self.view.isHidden = true
|
self.view.isHidden = true
|
||||||
} after: { [unowned self] in
|
} after: { [unowned self] in
|
||||||
self.credentialProvider.credentials(for: credentialIdentity)
|
credentialProvider.credentials(for: credentialIdentity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class ExtensionViewController: UIViewController {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
prepareCredentialList()
|
prepareCredentialList()
|
||||||
passcodelock.presentPasscodeLockIfNeeded(self, after: { [unowned self] in
|
passcodelock.presentPasscodeLockIfNeeded(self, after: { [unowned self] in
|
||||||
self.view.isHidden = false
|
view.isHidden = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,8 +191,8 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
||||||
let myContext = LAContext()
|
let myContext = LAContext()
|
||||||
// If the device passcode is not set, reset the app.
|
// If the device passcode is not set, reset the app.
|
||||||
guard myContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) else {
|
guard myContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) else {
|
||||||
self.passwordStore.erase()
|
passwordStore.erase()
|
||||||
self.passcodeLockDidSucceed()
|
passcodeLockDidSucceed()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// If the device passcode is set, authentication is required.
|
// If the device passcode is set, authentication is required.
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,14 @@ public struct GitCredential {
|
||||||
private func createCredentialProvider(_ passwordProvider: @escaping PasswordProvider) -> GTCredentialProvider {
|
private func createCredentialProvider(_ passwordProvider: @escaping PasswordProvider) -> GTCredentialProvider {
|
||||||
var attempts = 1
|
var attempts = 1
|
||||||
return GTCredentialProvider { _, _, _ -> GTCredential? in
|
return GTCredentialProvider { _, _, _ -> GTCredential? in
|
||||||
if attempts > self.credentialType.allowedAttempts {
|
if attempts > credentialType.allowedAttempts {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let password = self.getPassword(attempts: attempts, passwordProvider: passwordProvider) else {
|
guard let password = getPassword(attempts: attempts, passwordProvider: passwordProvider) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
attempts += 1
|
attempts += 1
|
||||||
return try? self.credentialType.createGTCredential(password: password)
|
return try? credentialType.createGTCredential(password: password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}"
|
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}"
|
||||||
SWIFTFORMAT_VERSION="0.50.*"
|
SWIFTFORMAT_VERSION="0.51.*"
|
||||||
|
|
||||||
if [[ "${CI}" == "true" ]]; then
|
if [[ "${CI}" == "true" ]]; then
|
||||||
echo "Running in a Continuous Integration environment. Formatting is skipped."
|
echo "Running in a Continuous Integration environment. Formatting is skipped."
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue