diff --git a/Source/Application/AppCoordinator.swift b/Source/Application/AppCoordinator.swift index 248b7be2..489c78a9 100644 --- a/Source/Application/AppCoordinator.swift +++ b/Source/Application/AppCoordinator.swift @@ -35,17 +35,47 @@ final class AppCoordinator: BaseCoordinator { private lazy var walletUseCase = useCaseProvider.makeWalletUseCase() private lazy var pincodeUseCase = useCaseProvider.makePincodeUseCase() - - init(navigationController: UINavigationController, deepLinkHandler: DeepLinkHandler, useCaseProvider: UseCaseProvider) { + private lazy var lockAppScene = LockAppScene() + + private lazy var unlockAppScene: UnlockAppWithPincode = { + let viewModel = UnlockAppWithPincodeViewModel(useCase: pincodeUseCase) + let scene = UnlockAppWithPincode(viewModel: viewModel) + + self.bag <~ scene.viewModel.navigator.navigation + .asObservable() + .observeOn(MainScheduler.asyncInstance) + .do(onNext: { [weak self] userDid in + switch userDid { + case .unlockApp: + self?.appIsUnlockedEmitBufferedDeeplinks() + self?.restoreMainNavigationStack() + } + }) + .asDriverOnErrorReturnEmpty() + .drive() + return scene + }() + + private let __setRootViewControllerOfWindow: (UIViewController) -> Void + private let isViewControllerRootOfWindow: (UIViewController) -> Bool + + init( + navigationController: UINavigationController, + deepLinkHandler: DeepLinkHandler, + useCaseProvider: UseCaseProvider, + isViewControllerRootOfWindow: @escaping (UIViewController) -> Bool, + setRootViewControllerOfWindow: @escaping (UIViewController) -> Void + ) { self.deepLinkHandler = deepLinkHandler self.useCaseProvider = useCaseProvider - + self.__setRootViewControllerOfWindow = setRootViewControllerOfWindow + self.isViewControllerRootOfWindow = isViewControllerRootOfWindow super.init(navigationController: navigationController) } override func start(didStart: Completion? = nil) { if walletUseCase.hasConfiguredWallet { - toMain(lockIfNeeded: true) + toMain(displayUnlockSceneIfNeeded: true) } else { toOnboarding() } @@ -69,7 +99,7 @@ private extension AppCoordinator { } } - func toMain(lockIfNeeded lock: Bool = false) { + func toMain(displayUnlockSceneIfNeeded displayUnlockScene: Bool = false) { let main = MainCoordinator( navigationController: navigationController, @@ -79,8 +109,8 @@ private extension AppCoordinator { ) start(coordinator: main, transition: .replace, didStart: { [unowned self] in - if lock { - self.lockApp() + if displayUnlockScene { + self.toUnlockAppWithPincodeIfNeeded() } }, navigationHandler: { [unowned self] userDid in switch userDid { @@ -89,42 +119,81 @@ private extension AppCoordinator { }) } + var hasConfiguredPincode: Bool { + return pincodeUseCase.hasConfiguredPincode + } + func toUnlockAppWithPincodeIfNeeded() { - guard pincodeUseCase.hasConfiguredPincode, !isCurrentlyPresentingLockScene else { return } - - let viewModel = UnlockAppWithPincodeViewModel(useCase: pincodeUseCase) - - deepLinkHandler.appIsLockedBufferDeeplinks() - - topMostCoordinator.modallyPresent( - scene: UnlockAppWithPincode.self, - viewModel: viewModel - ) { [unowned self] userDid, dismissScene in - switch userDid { - case .unlockApp: - dismissScene(true, { [unowned self] in - self.deepLinkHandler.appIsUnlockedEmitBufferedDeeplinks() - }) - } + + guard hasConfiguredPincode, !isCurrentlyPresentingUnLockScene else { + return } + + setRootViewControllerOfWindow(to: unlockAppScene) + } +} + +private extension AppCoordinator { + + func restoreMainNavigationStack() { + setRootViewControllerOfWindow(to: navigationController) + } + + func setRootViewControllerOfWindow(to viewController: UIViewController) { + __setRootViewControllerOfWindow(viewController) } } // MARK: - Lock app with pincode extension AppCoordinator { + func appWillResignActive() { lockApp() } + + func appDidBecomeActive() { + unlockApp() + } } // MARK: - Private Lock app with pincode private extension AppCoordinator { + func lockApp() { - toUnlockAppWithPincodeIfNeeded() + print("🔐 Trying to lock app") + if isCurrentlyPresentingUnLockScene || isCurrentlyPresentingLockScene { + print("🙅🏻‍♀️ Avoided locking app 🔒") + return + } + deepLinkHandler.appIsLockedBufferDeeplinks() + setRootViewControllerOfWindow(to: lockAppScene) + } + + func unlockApp() { + if isCurrentlyPresentingUnLockScene { return } + guard isCurrentlyPresentingLockScene else { return } + print("🔓 Trying to unlock app") + if hasConfiguredPincode { + toUnlockAppWithPincodeIfNeeded() + } else { + restoreMainNavigationStack() + appIsUnlockedEmitBufferedDeeplinks() + } } + var isCurrentlyPresentingUnLockScene: Bool { + guard hasConfiguredPincode else { return false } + return isViewControllerRootOfWindow(unlockAppScene) + } + var isCurrentlyPresentingLockScene: Bool { - return topMostScene is UnlockAppWithPincode + return isViewControllerRootOfWindow(lockAppScene) + } + + func appIsUnlockedEmitBufferedDeeplinks(delayInSeconds: TimeInterval = 0.2) { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) { [weak self] in + self?.deepLinkHandler.appIsUnlockedEmitBufferedDeeplinks() + } } } diff --git a/Source/Application/AppDelegate.swift b/Source/Application/AppDelegate.swift index 63bef4e3..6ec77dc1 100644 --- a/Source/Application/AppDelegate.swift +++ b/Source/Application/AppDelegate.swift @@ -30,12 +30,19 @@ class AppDelegate: UIResponder { fileprivate lazy var appCoordinator: AppCoordinator = { let navigationController = NavigationBarLayoutingNavigationController() + window?.rootViewController = navigationController return AppCoordinator( navigationController: navigationController, deepLinkHandler: DeepLinkHandler(), - useCaseProvider: DefaultUseCaseProvider.shared + useCaseProvider: DefaultUseCaseProvider.shared, + isViewControllerRootOfWindow: { [weak self] in + self?.window?.rootViewController == $0 + }, + setRootViewControllerOfWindow: { [weak self] in + self?.window?.rootViewController = $0 + } ) }() } @@ -51,8 +58,10 @@ extension AppDelegate: UIApplicationDelegate { func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard - userActivity.activityType == NSUserActivityTypeBrowsingWeb, let incomingURL = userActivity.webpageURL else { - return false + userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingURL = userActivity.webpageURL + else { + return false } return appCoordinator.handleDeepLink(incomingURL) @@ -61,4 +70,8 @@ extension AppDelegate: UIApplicationDelegate { func applicationWillResignActive(_ application: UIApplication) { appCoordinator.appWillResignActive() } + + func applicationDidBecomeActive(_ application: UIApplication) { + appCoordinator.appDidBecomeActive() + } } diff --git a/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Present.swift b/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Present.swift index 49311327..d50349fa 100644 --- a/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Present.swift +++ b/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Present.swift @@ -39,7 +39,9 @@ extension Coordinating { scene _: S.Type, viewModel: V.ViewModel, animated: Bool = true, - navigationHandler: @escaping (_ step: V.ViewModel.NavigationStep, _ dismiss: DismissScene) -> Void + presentationCompletion: Completion? = nil, +// navigationHandler: @escaping (_ step: V.ViewModel.NavigationStep, _ dismiss: @escaping DismissScene) -> Void + navigationHandler: @escaping NavigationHandlerModalScene ) where S: Scene, V: ContentView, V.ViewModel: Navigating { // Create a new instance of the `Scene`, injecting its ViewModel @@ -48,6 +50,7 @@ extension Coordinating { modallyPresent( scene: scene, animated: animated, + presentationCompletion: presentationCompletion, navigationHandler: navigationHandler ) } @@ -58,13 +61,13 @@ extension Coordinating { /// - scene: A `Scene` (UIViewController) to present. /// - animated: Whether to animate the presentation of the scene or not. /// - navigationHandler: **Required** closure handling the navigation steps emitted by the scene's ViewModel. - /// - step: The navigation steps emitted by the `viewmodel` - /// - dismiss: Closure you **should** invoke when you want to dimiss the scene. func modallyPresent( scene: S, animated: Bool = true, - navigationHandler: @escaping (_ step: V.ViewModel.NavigationStep, _ dismiss: DismissScene) -> Void - ) where S: Scene, V: ContentView, V.ViewModel: Navigating { + presentationCompletion: Completion? = nil, +// navigationHandler: @escaping (_ step: V.ViewModel.NavigationStep, _ dismiss: @escaping DismissScene) -> Void + navigationHandler: @escaping NavigationHandlerModalScene + ) where S: Scene, V: ContentView, V.ViewModel: Navigating { let viewModel = scene.viewModel @@ -72,7 +75,7 @@ extension Coordinating { // Since this is starting a new modal flow we should use a new NavigationController. let viewControllerToPresent = NavigationBarLayoutingNavigationController(rootViewController: scene) - navigationController.present(viewControllerToPresent, animated: true, completion: nil) + navigationController.present(viewControllerToPresent, animated: animated, completion: presentationCompletion) // Subscribe to the navigation steps emitted by the viewModel's navigator // And invoke the navigationHandler closure passed in to this method diff --git a/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Push.swift b/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Push.swift index 7e280207..058b587e 100644 --- a/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Push.swift +++ b/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Push.swift @@ -86,26 +86,3 @@ extension Coordinating { }).drive() } } - -private extension UINavigationController { - func setRootViewControllerIfEmptyElsePush( - viewController: UIViewController, - animated: Bool, - completion: Completion? = nil - ) { - - if viewControllers.isEmpty { - setViewControllers([viewController], animated: false) - } else { - pushViewController(viewController, animated: animated) - } - - // Add extra functionality to pass a "completion" closure even for `push`ed ViewControllers. - guard let completion = completion else { return } - guard animated, let coordinator = transitionCoordinator else { - DispatchQueue.main.async { completion() } - return - } - coordinator.animate(alongsideTransition: nil) { _ in completion() } - } -} diff --git a/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Replace.swift b/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Replace.swift new file mode 100644 index 00000000..c21da0b5 --- /dev/null +++ b/Source/Application/Navigation/Coordinator/Coordinating/Coordinating+Scene/Coordinating+Scene+Replace.swift @@ -0,0 +1,125 @@ +// +// MIT License +// +// Copyright (c) 2018-2019 Open Zesame (https://github.com/OpenZesame) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import UIKit + +// MARKL - REPLACE +extension Coordinating { + + typealias NavigationHandlerModalScene = (N.NavigationStep, @escaping DismissScene) -> Void + + /// This method is used to replace ALL scenes in the navigation stack in current coordinating context. + /// + /// - Parameters: + /// - scene: A `Scene` (UIViewController) to set as the single VC. + /// - animated: Whether to animate the presentation of the scene or not. + /// - navigationHandler: **Required** closure handling the navigation steps emitted by the scene's ViewModel. + func replaceAllScenes( + with _: S.Type, + viewModel: V.ViewModel, + animated: Bool = true, + whenReplacingFinished: Completion? = nil, + navigationHandler: @escaping NavigationHandlerModalScene +// navigationHandler: @escaping (_ step: V.ViewModel.NavigationStep, _ dismiss: @escaping DismissScene) -> Void + ) where S: Scene, V: ContentView, V.ViewModel: Navigating { + + // Create a new instance of the `Scene`, injecting its ViewModel + let scene = S.init(viewModel: viewModel) + + replaceAllScenes( + with: scene, + animated: animated, + whenReplacingFinished: whenReplacingFinished, + navigationHandler: navigationHandler + ) + } + + /// This method is used to replace ALL scenes in the navigation stack in current coordinating context. + /// + /// - Parameters: + /// - scene: A `Scene` (UIViewController) to set as the single VC. + /// - animated: Whether to animate the presentation of the scene or not. + /// - navigationHandler: **Required** closure handling the navigation steps emitted by the scene's ViewModel. + func replaceAllScenes( + with scene: S, + animated: Bool = true, + whenReplacingFinished: Completion? = nil, +// navigationHandler: @escaping (_ step: V.ViewModel.NavigationStep, _ dismiss: @escaping DismissScene) -> Void + navigationHandler: @escaping NavigationHandlerModalScene + ) where S: Scene, V: ContentView, V.ViewModel: Navigating { + + let viewModel = scene.viewModel + + let oldVCs = navigationController.viewControllers + + self.navigationController.setRootViewControllerIfEmptyElsePush(viewController: scene, animated: animated, forceReplaceAllVCsInsteadOfPush: true) { + whenReplacingFinished?() + oldVCs.forEach { $0.dismiss(animated: false, completion: nil) } + } + + bag <~ viewModel.navigator.navigation.do(onNext: { + navigationHandler($0, { [unowned scene] animated, navigationCompletion in + print("⛱ replaceAllScenes dismissCompletion of navigationHandler") + scene.dismiss(animated: animated, completion: navigationCompletion) + }) + }).drive() + } +} + +extension UINavigationController { + + func setRootViewControllerIfEmptyElsePush( + viewController: UIViewController, + animated: Bool = true, + forceReplaceAllVCsInsteadOfPush: Bool = false, + completion: Completion? = nil + ) { + + if viewControllers.isEmpty || forceReplaceAllVCsInsteadOfPush { + print("replacing VCs with one of type: \(type(of: viewController))") + setViewControllers([viewController], animated: animated) + } else { + pushViewController(viewController, animated: animated) + } + + // Add extra functionality to pass a "completion" closure even for `push`ed ViewControllers. + guard let completion = completion else { return } + guard animated, let coordinator = transitionCoordinator else { + DispatchQueue.main.async { completion() } + return + } + coordinator.animate(alongsideTransition: nil) { _ in completion() } + } +} + +extension UINavigationController { + func popToRootViewController(animated: Bool = true, completion: @escaping Completion) { + popToRootViewController(animated: animated) + guard animated, let coordinator = transitionCoordinator else { + DispatchQueue.main.async { completion() } + return + } + coordinator.animate(alongsideTransition: nil) { _ in completion() } + } +} diff --git a/Source/Application/Navigation/DeepLinking/DeepLinkHandler.swift b/Source/Application/Navigation/DeepLinking/DeepLinkHandler.swift index feb53dbf..b7efb7bd 100644 --- a/Source/Application/Navigation/DeepLinking/DeepLinkHandler.swift +++ b/Source/Application/Navigation/DeepLinking/DeepLinkHandler.swift @@ -41,6 +41,7 @@ final class DeepLinkHandler { func appIsLockedBufferDeeplinks() { appIsLockedSoBufferLink = true } + func appIsUnlockedEmitBufferedDeeplinks() { defer { bufferedLink = nil } appIsLockedSoBufferLink = false diff --git a/Source/Controller/SceneController.swift b/Source/Controller/SceneController.swift index 8fe840ce..e407ef1e 100644 --- a/Source/Controller/SceneController.swift +++ b/Source/Controller/SceneController.swift @@ -145,6 +145,7 @@ private extension SceneController { } func applyLayoutIfNeeded() { + guard let navigationController = navigationController else { return } guard let barLayoutingNavController = navigationController as? NavigationBarLayoutingNavigationController else { incorrectImplementation("navigationController should be instance of `NavigationBarLayoutingNavigationController`") } diff --git a/Source/Scenes/0_Onboarding/6_ChoosePincode/1_ConfirmNewPincode/ConfirmNewPincodeView.swift b/Source/Scenes/0_Onboarding/6_ChoosePincode/1_ConfirmNewPincode/ConfirmNewPincodeView.swift index 9ba21bb3..67a3fc97 100644 --- a/Source/Scenes/0_Onboarding/6_ChoosePincode/1_ConfirmNewPincode/ConfirmNewPincodeView.swift +++ b/Source/Scenes/0_Onboarding/6_ChoosePincode/1_ConfirmNewPincode/ConfirmNewPincodeView.swift @@ -30,7 +30,7 @@ private typealias € = L10n.Scene.ConfirmNewPincode final class ConfirmNewPincodeView: ScrollableStackViewOwner { - private lazy var inputPincodeView = InputPincodeView() + private lazy var inputPincodeView = InputPincodeView(isClearedOnValidInput: false) private lazy var haveBackedUpPincodeCheckbox = CheckboxWithLabel() private lazy var confirmPincodeButton = UIButton() diff --git a/Source/Scenes/1_Main/0_LockApp/LockAppScene.swift b/Source/Scenes/1_Main/0_LockApp/LockAppScene.swift new file mode 100644 index 00000000..3054047b --- /dev/null +++ b/Source/Scenes/1_Main/0_LockApp/LockAppScene.swift @@ -0,0 +1,48 @@ +// +// MIT License +// +// Copyright (c) 2018-2019 Open Zesame (https://github.com/OpenZesame) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import UIKit + +final class LockAppScene: AbstractController { + private lazy var motionEffectAuroraImageView = UIView() + private lazy var titleLabel = UILabel() + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .black + view.addSubview(motionEffectAuroraImageView) + motionEffectAuroraImageView.addSubview(titleLabel) + motionEffectAuroraImageView.edgesToSuperview() + titleLabel.centerInSuperview() + + let appName = Bundle.main.name ?? "Zhip" + + titleLabel.withStyle(.impression) { + $0.font(.bigBang).text(appName).textColor(.white) + } + + addAuroraImagesWithMotionEffect(to: motionEffectAuroraImageView) + motionEffectAuroraImageView.bringSubviewToFront(titleLabel) + } +} + diff --git a/Source/Scenes/1_Main/0_UnlockAppWithPincode/UnlockAppWithPincode.swift b/Source/Scenes/1_Main/1_UnlockAppWithPincode/UnlockAppWithPincode.swift similarity index 100% rename from Source/Scenes/1_Main/0_UnlockAppWithPincode/UnlockAppWithPincode.swift rename to Source/Scenes/1_Main/1_UnlockAppWithPincode/UnlockAppWithPincode.swift diff --git a/Source/Scenes/1_Main/0_UnlockAppWithPincode/UnlockAppWithPincodeView.swift b/Source/Scenes/1_Main/1_UnlockAppWithPincode/UnlockAppWithPincodeView.swift similarity index 100% rename from Source/Scenes/1_Main/0_UnlockAppWithPincode/UnlockAppWithPincodeView.swift rename to Source/Scenes/1_Main/1_UnlockAppWithPincode/UnlockAppWithPincodeView.swift diff --git a/Source/Scenes/1_Main/0_UnlockAppWithPincode/UnlockAppWithPincodeViewModel.swift b/Source/Scenes/1_Main/1_UnlockAppWithPincode/UnlockAppWithPincodeViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/0_UnlockAppWithPincode/UnlockAppWithPincodeViewModel.swift rename to Source/Scenes/1_Main/1_UnlockAppWithPincode/UnlockAppWithPincodeViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/0_Main/Main.swift b/Source/Scenes/1_Main/2_Main/0_Main/Main.swift similarity index 94% rename from Source/Scenes/1_Main/1_Main/0_Main/Main.swift rename to Source/Scenes/1_Main/2_Main/0_Main/Main.swift index 917044c2..a331c98e 100644 --- a/Source/Scenes/1_Main/1_Main/0_Main/Main.swift +++ b/Source/Scenes/1_Main/2_Main/0_Main/Main.swift @@ -35,3 +35,8 @@ extension Main: NavigationBarLayoutOwner { return .transluscent } } + + + +//final class AppLocked: Scene {} +//final class AppLockedView: UIView {} diff --git a/Source/Scenes/1_Main/1_Main/0_Main/MainView.swift b/Source/Scenes/1_Main/2_Main/0_Main/MainView.swift similarity index 91% rename from Source/Scenes/1_Main/1_Main/0_Main/MainView.swift rename to Source/Scenes/1_Main/2_Main/0_Main/MainView.swift index 00c46b5d..8a99e2f3 100644 --- a/Source/Scenes/1_Main/1_Main/0_Main/MainView.swift +++ b/Source/Scenes/1_Main/2_Main/0_Main/MainView.swift @@ -108,17 +108,17 @@ private extension MainView { insertSubview(motionEffectAuroraImageView, belowSubview: scrollView) motionEffectAuroraImageView.edgesToSuperview() - setupAuroraImageViewWithMotionEffect() + addAuroraImagesWithMotionEffect(to: motionEffectAuroraImageView) } +} - func setupAuroraImageViewWithMotionEffect() { - motionEffectAuroraImageView.backgroundColor = .clear - motionEffectAuroraImageView.translatesAutoresizingMaskIntoConstraints = false - - motionEffectAuroraImageView.addMotionEffectFromImageAssets( - front: Image.frontAurora, - middle: Image.middleAurora, - back: Image.backAurora - ) - } +func addAuroraImagesWithMotionEffect(to effectView: UIView) { + effectView.backgroundColor = .clear + effectView.translatesAutoresizingMaskIntoConstraints = false + + effectView.addMotionEffectFromImageAssets( + front: Image.frontAurora, + middle: Image.middleAurora, + back: Image.backAurora + ) } diff --git a/Source/Scenes/1_Main/1_Main/0_Main/MainViewModel.swift b/Source/Scenes/1_Main/2_Main/0_Main/MainViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/0_Main/MainViewModel.swift rename to Source/Scenes/1_Main/2_Main/0_Main/MainViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransaction.swift b/Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransaction.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransaction.swift rename to Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransaction.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransactionView.swift b/Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransactionView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransactionView.swift rename to Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransactionView.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransactionViewModel.swift b/Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransactionViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransactionViewModel.swift rename to Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/0_PrepareTransaction/PrepareTransactionViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeController.swift b/Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeController.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeController.swift rename to Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeController.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeView.swift b/Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeView.swift rename to Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeView.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeViewModel.swift b/Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeViewModel.swift rename to Source/Scenes/1_Main/2_Main/A_Send/1_PrepareTransaction/1_ScanQR/ScanQRCodeViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigning.swift b/Source/Scenes/1_Main/2_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigning.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigning.swift rename to Source/Scenes/1_Main/2_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigning.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigningView.swift b/Source/Scenes/1_Main/2_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigningView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigningView.swift rename to Source/Scenes/1_Main/2_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigningView.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigningViewModel.swift b/Source/Scenes/1_Main/2_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigningViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigningViewModel.swift rename to Source/Scenes/1_Main/2_Main/A_Send/2_ReviewTransaction/ReviewTransactionBeforeSigningViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/3_SignTransaction/SignTransaction.swift b/Source/Scenes/1_Main/2_Main/A_Send/3_SignTransaction/SignTransaction.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/3_SignTransaction/SignTransaction.swift rename to Source/Scenes/1_Main/2_Main/A_Send/3_SignTransaction/SignTransaction.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/3_SignTransaction/SignTransactionView.swift b/Source/Scenes/1_Main/2_Main/A_Send/3_SignTransaction/SignTransactionView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/3_SignTransaction/SignTransactionView.swift rename to Source/Scenes/1_Main/2_Main/A_Send/3_SignTransaction/SignTransactionView.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/3_SignTransaction/SignTransactionViewModel.swift b/Source/Scenes/1_Main/2_Main/A_Send/3_SignTransaction/SignTransactionViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/3_SignTransaction/SignTransactionViewModel.swift rename to Source/Scenes/1_Main/2_Main/A_Send/3_SignTransaction/SignTransactionViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusController.swift b/Source/Scenes/1_Main/2_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusController.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusController.swift rename to Source/Scenes/1_Main/2_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusController.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusView.swift b/Source/Scenes/1_Main/2_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusView.swift rename to Source/Scenes/1_Main/2_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusView.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusViewModel.swift b/Source/Scenes/1_Main/2_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusViewModel.swift rename to Source/Scenes/1_Main/2_Main/A_Send/4_PollTransactionStatus/PollTransactionStatusViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/A_Send/SendCoordinator.swift b/Source/Scenes/1_Main/2_Main/A_Send/SendCoordinator.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/A_Send/SendCoordinator.swift rename to Source/Scenes/1_Main/2_Main/A_Send/SendCoordinator.swift diff --git a/Source/Scenes/1_Main/1_Main/B_Receive/Receive/Receive.swift b/Source/Scenes/1_Main/2_Main/B_Receive/Receive/Receive.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/B_Receive/Receive/Receive.swift rename to Source/Scenes/1_Main/2_Main/B_Receive/Receive/Receive.swift diff --git a/Source/Scenes/1_Main/1_Main/B_Receive/Receive/ReceiveView.swift b/Source/Scenes/1_Main/2_Main/B_Receive/Receive/ReceiveView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/B_Receive/Receive/ReceiveView.swift rename to Source/Scenes/1_Main/2_Main/B_Receive/Receive/ReceiveView.swift diff --git a/Source/Scenes/1_Main/1_Main/B_Receive/Receive/ReceiveViewModel.swift b/Source/Scenes/1_Main/2_Main/B_Receive/Receive/ReceiveViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/B_Receive/Receive/ReceiveViewModel.swift rename to Source/Scenes/1_Main/2_Main/B_Receive/Receive/ReceiveViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/B_Receive/ReceiveCoordinator.swift b/Source/Scenes/1_Main/2_Main/B_Receive/ReceiveCoordinator.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/B_Receive/ReceiveCoordinator.swift rename to Source/Scenes/1_Main/2_Main/B_Receive/ReceiveCoordinator.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/Settings.swift b/Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/Settings.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/Settings.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/Settings.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/SettingsItem.swift b/Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/SettingsItem.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/SettingsItem.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/SettingsItem.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/SettingsTableViewCell.swift b/Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/SettingsTableViewCell.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/SettingsTableViewCell.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/SettingsTableViewCell.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/SettingsView.swift b/Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/SettingsView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/SettingsView.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/SettingsView.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/SettingsViewModel.swift b/Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/SettingsViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/0_Settings/SettingsViewModel.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/0_Settings/SettingsViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/A_RemovePincode/RemovePincode.swift b/Source/Scenes/1_Main/2_Main/C_Settings/A_RemovePincode/RemovePincode.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/A_RemovePincode/RemovePincode.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/A_RemovePincode/RemovePincode.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/A_RemovePincode/RemovePincodeView.swift b/Source/Scenes/1_Main/2_Main/C_Settings/A_RemovePincode/RemovePincodeView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/A_RemovePincode/RemovePincodeView.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/A_RemovePincode/RemovePincodeView.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/A_RemovePincode/RemovePincodeViewModel.swift b/Source/Scenes/1_Main/2_Main/C_Settings/A_RemovePincode/RemovePincodeViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/A_RemovePincode/RemovePincodeViewModel.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/A_RemovePincode/RemovePincodeViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalController.swift b/Source/Scenes/1_Main/2_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalController.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalController.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalController.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalView.swift b/Source/Scenes/1_Main/2_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalView.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalView.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalView.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalViewModel.swift b/Source/Scenes/1_Main/2_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalViewModel.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalViewModel.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/B_ConfirmWalletRemoval/ConfirmWalletRemovalViewModel.swift diff --git a/Source/Scenes/1_Main/1_Main/C_Settings/SettingsCoordinator.swift b/Source/Scenes/1_Main/2_Main/C_Settings/SettingsCoordinator.swift similarity index 100% rename from Source/Scenes/1_Main/1_Main/C_Settings/SettingsCoordinator.swift rename to Source/Scenes/1_Main/2_Main/C_Settings/SettingsCoordinator.swift diff --git a/Source/Views/SpecificViews/InputPincodeView/InputPincodeView.swift b/Source/Views/SpecificViews/InputPincodeView/InputPincodeView.swift index 47ae5b51..7c982c76 100644 --- a/Source/Views/SpecificViews/InputPincodeView/InputPincodeView.swift +++ b/Source/Views/SpecificViews/InputPincodeView/InputPincodeView.swift @@ -35,8 +35,9 @@ final class InputPincodeView: UIView { private lazy var stackView = UIStackView(arrangedSubviews: [pinField, errorLabel]) private let hapticFeedbackGenerator = UINotificationFeedbackGenerator() - - init() { + private let isClearedOnValidInput: Bool + init(isClearedOnValidInput: Bool = true) { + self.isClearedOnValidInput = isClearedOnValidInput super.init(frame: .zero) setup() } @@ -47,8 +48,13 @@ final class InputPincodeView: UIView { pinField.validate(validation) switch validation { - case .valid: vibrateOnValid(); fallthrough - case .empty: errorLabel.text = nil + case .valid: + vibrateOnValid(); fallthrough + case .empty: + errorLabel.text = nil + if isClearedOnValidInput { + pinField.clearInput() + } case .errorMessage(let errorMessage): vibrateOnInvalid() errorLabel.text = errorMessage diff --git a/Zhip.xcodeproj/project.pbxproj b/Zhip.xcodeproj/project.pbxproj index 3c948c36..e804213c 100644 --- a/Zhip.xcodeproj/project.pbxproj +++ b/Zhip.xcodeproj/project.pbxproj @@ -310,6 +310,8 @@ E682358B22BB86620028F1EC /* ReviewTransactionBeforeSigningViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E682358822BB86620028F1EC /* ReviewTransactionBeforeSigningViewModel.swift */; }; E682358C22BB86620028F1EC /* ReviewTransactionBeforeSigningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E682358922BB86620028F1EC /* ReviewTransactionBeforeSigningView.swift */; }; E682358D22BB86620028F1EC /* ReviewTransactionBeforeSigning.swift in Sources */ = {isa = PBXBuildFile; fileRef = E682358A22BB86620028F1EC /* ReviewTransactionBeforeSigning.swift */; }; + E682358F22BBC8E00028F1EC /* Coordinating+Scene+Replace.swift in Sources */ = {isa = PBXBuildFile; fileRef = E682358E22BBC8E00028F1EC /* Coordinating+Scene+Replace.swift */; }; + E682359222BC242E0028F1EC /* LockAppScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = E682359122BC242E0028F1EC /* LockAppScene.swift */; }; E6942A4E2262180700778FF6 /* libRxCocoa.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E6942A4D2262180700778FF6 /* libRxCocoa.a */; }; E6942A502262180700778FF6 /* libRxSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E6942A4F2262180700778FF6 /* libRxSwift.a */; }; E6942A522262180700778FF6 /* libRxTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E6942A512262180700778FF6 /* libRxTest.a */; }; @@ -648,6 +650,8 @@ E682358822BB86620028F1EC /* ReviewTransactionBeforeSigningViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReviewTransactionBeforeSigningViewModel.swift; sourceTree = ""; }; E682358922BB86620028F1EC /* ReviewTransactionBeforeSigningView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReviewTransactionBeforeSigningView.swift; sourceTree = ""; }; E682358A22BB86620028F1EC /* ReviewTransactionBeforeSigning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReviewTransactionBeforeSigning.swift; sourceTree = ""; }; + E682358E22BBC8E00028F1EC /* Coordinating+Scene+Replace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Coordinating+Scene+Replace.swift"; sourceTree = ""; }; + E682359122BC242E0028F1EC /* LockAppScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockAppScene.swift; sourceTree = ""; }; E6942A4D2262180700778FF6 /* libRxCocoa.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRxCocoa.a; sourceTree = BUILT_PRODUCTS_DIR; }; E6942A4F2262180700778FF6 /* libRxSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRxSwift.a; sourceTree = BUILT_PRODUCTS_DIR; }; E6942A512262180700778FF6 /* libRxTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libRxTest.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -861,6 +865,7 @@ children = ( 48241B4821B153EA0054ED8E /* Coordinating+Scene+Present.swift */, 48ECF60D21A1D0AC0078D659 /* Coordinating+Scene+Push.swift */, + E682358E22BBC8E00028F1EC /* Coordinating+Scene+Replace.swift */, 48241B4E21B158B60054ED8E /* Coordinating+NavigationStack.swift */, ); path = "Coordinating+Scene"; @@ -1521,13 +1526,14 @@ isa = PBXGroup; children = ( 48ECF5FE21A18F5B0078D659 /* MainCoordinator.swift */, - 48ECF59421A18F140078D659 /* 0_UnlockAppWithPincode */, - 48ECF57421A18F140078D659 /* 1_Main */, + E682359022BC240A0028F1EC /* 0_LockApp */, + 48ECF59421A18F140078D659 /* 1_UnlockAppWithPincode */, + 48ECF57421A18F140078D659 /* 2_Main */, ); path = 1_Main; sourceTree = ""; }; - 48ECF57421A18F140078D659 /* 1_Main */ = { + 48ECF57421A18F140078D659 /* 2_Main */ = { isa = PBXGroup; children = ( 48ECF58921A18F140078D659 /* 0_Main */, @@ -1535,7 +1541,7 @@ 48ECF58E21A18F140078D659 /* B_Receive */, 48ECF57F21A18F140078D659 /* C_Settings */, ); - path = 1_Main; + path = 2_Main; sourceTree = ""; }; 48ECF57521A18F140078D659 /* A_Send */ = { @@ -1631,14 +1637,14 @@ path = Receive; sourceTree = ""; }; - 48ECF59421A18F140078D659 /* 0_UnlockAppWithPincode */ = { + 48ECF59421A18F140078D659 /* 1_UnlockAppWithPincode */ = { isa = PBXGroup; children = ( 48ECF59521A18F140078D659 /* UnlockAppWithPincodeView.swift */, 48ECF59621A18F140078D659 /* UnlockAppWithPincodeViewModel.swift */, 48ECF59721A18F140078D659 /* UnlockAppWithPincode.swift */, ); - path = 0_UnlockAppWithPincode; + path = 1_UnlockAppWithPincode; sourceTree = ""; }; 48ECF59821A18F140078D659 /* 0_Onboarding */ = { @@ -1891,6 +1897,14 @@ path = 2_ReviewTransaction; sourceTree = ""; }; + E682359022BC240A0028F1EC /* 0_LockApp */ = { + isa = PBXGroup; + children = ( + E682359122BC242E0028F1EC /* LockAppScene.swift */, + ); + path = 0_LockApp; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -2158,6 +2172,7 @@ files = ( E641443B225E76AA008C146D /* ReceiveViewModel.swift in Sources */, 483DDECC219CB860009D3F4C /* InputType.swift in Sources */, + E682359222BC242E0028F1EC /* LockAppScene.swift in Sources */, 48053F3321EBDCA2008FCE13 /* FooterView.swift in Sources */, 48ECF61E21A2A02E0078D659 /* SingleCellTypeTableView.swift in Sources */, E6019A60225E754D0079DDE8 /* ScanQRCodeViewModel.swift in Sources */, @@ -2242,6 +2257,7 @@ 489EB12E21CA459000664AA7 /* UIImageView+Styling.swift in Sources */, 485AAC9C21AAC6EB005C56CC /* AmountValidator.swift in Sources */, 4847EDCC215FDC96002FF978 /* DefaultUseCaseProvider.swift in Sources */, + E682358F22BBC8E00028F1EC /* Coordinating+Scene+Replace.swift in Sources */, 48ECF4B221A06C4D0078D659 /* SpinnerView.swift in Sources */, E635A4B0225D301B00124823 /* ChooseWalletView.swift in Sources */, 4847EDCF215FE914002FF978 /* DefaultTransactionsUseCase.swift in Sources */,