From 175058bf3444d17c0cfad5f6e3c861af207ebd3d Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 24 Oct 2021 13:53:08 +0300 Subject: [PATCH] Handle Realm init error with a modal --- FlowCrypt.xcodeproj/project.pbxproj | 4 + .../InvalidStorageViewController.swift | 74 +++++++++++++++++++ .../Encrypted Storage/EncryptedStorage.swift | 13 +++- .../Functionality/Services/GlobalRouter.swift | 21 +++++- .../Resources/en.lproj/Localizable.strings | 6 ++ 5 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 FlowCrypt/Controllers/Bootstrap/InvalidStorageViewController.swift diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 9076fb10e..5fa5b19f8 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 21FEE26626FDD91A00E3783F /* ComposeMessageAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FEE26526FDD91A00E3783F /* ComposeMessageAttachment.swift */; }; 2C2A3B4B2719EE6100B7F27B /* KeyServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A3B4A2719EE6100B7F27B /* KeyServiceTests.swift */; }; 2C2A3B4D2719EF7300B7F27B /* PassPhraseServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A3B4C2719EF7300B7F27B /* PassPhraseServiceMock.swift */; }; + 2C60AB0C272564D40040D7F2 /* InvalidStorageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C60AB0B272564D40040D7F2 /* InvalidStorageViewController.swift */; }; 32DCA00224982EDA88D69C6E /* AppErr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA4B11D4531B3B04D01D1 /* AppErr.swift */; }; 32DCA04CA0DAB79C39514782 /* CoreTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCAC732B988D9704658812 /* CoreTypes.swift */; }; 32DCA1414EEA727B86C337D5 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA0C3D34A69851A238E87 /* Core.swift */; }; @@ -442,6 +443,7 @@ 21FEE26526FDD91A00E3783F /* ComposeMessageAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeMessageAttachment.swift; sourceTree = ""; }; 2C2A3B4A2719EE6100B7F27B /* KeyServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyServiceTests.swift; sourceTree = ""; }; 2C2A3B4C2719EF7300B7F27B /* PassPhraseServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassPhraseServiceMock.swift; sourceTree = ""; }; + 2C60AB0B272564D40040D7F2 /* InvalidStorageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvalidStorageViewController.swift; sourceTree = ""; }; 32DCA058652FD4616FB04FB6 /* SequenceExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SequenceExtensions.swift; sourceTree = ""; }; 32DCA0C3D34A69851A238E87 /* Core.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = ""; }; 32DCA0E63F2F0473D0A8EDB0 /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; }; @@ -1912,6 +1914,7 @@ isa = PBXGroup; children = ( D24F4C2123E2359B00C5EEE4 /* BootstrapViewController.swift */, + 2C60AB0B272564D40040D7F2 /* InvalidStorageViewController.swift */, ); path = Bootstrap; sourceTree = ""; @@ -2578,6 +2581,7 @@ 32DCA1B95DDC04D671F662F8 /* URLSessionExtension.swift in Sources */, 214A023A26A3029700C24066 /* EmailKeyManagerApi.swift in Sources */, D2E26F7424F2705B00612AF1 /* ContactDetailDecorator.swift in Sources */, + 2C60AB0C272564D40040D7F2 /* InvalidStorageViewController.swift in Sources */, 21C7DF0526697DA500C44800 /* PromiseKitExtension.swift in Sources */, 9F7E8F19269C538E0021C07F /* NavigationChildController.swift in Sources */, 9FB22CD625715CA10026EE64 /* BackupServiceErrorHandler.swift in Sources */, diff --git a/FlowCrypt/Controllers/Bootstrap/InvalidStorageViewController.swift b/FlowCrypt/Controllers/Bootstrap/InvalidStorageViewController.swift new file mode 100644 index 000000000..a17849a06 --- /dev/null +++ b/FlowCrypt/Controllers/Bootstrap/InvalidStorageViewController.swift @@ -0,0 +1,74 @@ +// +// InvalidStorageViewController.swift +// FlowCrypt +// +// Created by  Ivan Ushakov on 24.10.2021 +// Copyright © 2017-present FlowCrypt a. s. All rights reserved. +// + +import AsyncDisplayKit +import FlowCryptUI + +final class InvalidStorageViewController: TableNodeViewController { + private let encryptedStorage: EncryptedStorageType + private let router: GlobalRouterType + + init(encryptedStorage: EncryptedStorageType, router: GlobalRouterType) { + self.encryptedStorage = encryptedStorage + self.router = router + super.init(node: TableNode()) + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + title = "invalid_storage_title".localized + + node.delegate = self + node.dataSource = self + node.reloadData() + } + + private func handleTap() { + do { + try encryptedStorage.reset() + router.proceed() + } catch { + showAlert(message: "invalid_storage_reset_error".localized) + } + } +} + +extension InvalidStorageViewController: ASTableDelegate, ASTableDataSource { + func tableNode(_: ASTableNode, numberOfRowsInSection _: Int) -> Int { + 2 + } + + func tableNode(_: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { + { + if indexPath.row == 0 { + return KeyTextCellNode( + title: "invalid_storage_text".localized.attributed(.regular(12), color: .black), + insets: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) + ) + } + + if indexPath.row == 1 { + let input = ButtonCellNode.Input( + title: "invalid_storage_reset_button".localized.attributed(.bold(16), color: .white), + insets: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16), + color: .red + ) + return ButtonCellNode(input: input) { [weak self] in + self?.handleTap() + } + } + + return ASCellNode() + } + } +} diff --git a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift index 3be3512c4..9d10b551d 100644 --- a/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift +++ b/FlowCrypt/Functionality/DataManager/Encrypted Storage/EncryptedStorage.swift @@ -20,6 +20,8 @@ protocol EncryptedStorageType: KeyStorageType { var activeUser: UserObject? { get } func doesAnyKeyExist(for email: String) -> Bool + func validate() throws + func reset() throws func cleanup() } @@ -79,7 +81,6 @@ final class EncryptedStorage: EncryptedStorageType { let realm = try Realm(configuration: encryptedConfiguration) return realm } catch { -// destroyEncryptedStorage() - todo - give user option to wipe, don't do it automatically fatalError("failed to initiate realm: \(error)") } } @@ -254,6 +255,16 @@ extension EncryptedStorage { } extension EncryptedStorage { + func validate() throws { + Realm.Configuration.defaultConfiguration = encryptedConfiguration + _ = try Realm(configuration: encryptedConfiguration) + } + + func reset() throws { + let path = getDocumentDirectory() + "/" + Constants.encryptedDbFilename + try FileManager.default.removeItem(atPath: path) + } + func cleanup() { do { try storage.write { diff --git a/FlowCrypt/Functionality/Services/GlobalRouter.swift b/FlowCrypt/Functionality/Services/GlobalRouter.swift index e9286e976..9bd17f0a0 100644 --- a/FlowCrypt/Functionality/Services/GlobalRouter.swift +++ b/FlowCrypt/Functionality/Services/GlobalRouter.swift @@ -56,8 +56,25 @@ final class GlobalRouter: GlobalRouterType { extension GlobalRouter { /// proceed to flow (signing/setup/app) depends on user status (isLoggedIn/isSetupFinished) func proceed() { - userAccountService.cleanupSessions() - proceed(with: nil) + validateEncryptedStorage { + userAccountService.cleanupSessions() + proceed(with: nil) + } + } + + private func validateEncryptedStorage(_ completion: () -> Void) { + let storage = EncryptedStorage() + do { + try storage.validate() + completion() + } catch { + let controller = InvalidStorageViewController( + encryptedStorage: storage, + router: self + ) + keyWindow.rootViewController = UINavigationController(rootViewController: controller) + keyWindow.makeKeyAndVisible() + } } private func proceed(with session: SessionType?) { diff --git a/FlowCrypt/Resources/en.lproj/Localizable.strings b/FlowCrypt/Resources/en.lproj/Localizable.strings index d55756fba..861797165 100644 --- a/FlowCrypt/Resources/en.lproj/Localizable.strings +++ b/FlowCrypt/Resources/en.lproj/Localizable.strings @@ -251,3 +251,9 @@ "files_picking_photos_error_message" = "Could not add a photo"; "files_picking_size_error_message" = "Total attachments size can not exceed 10 MB"; +// Invalid storage view controller +"invalid_storage_title" = "Error"; +"invalid_storage_text" = "Your data storage is invalid. You could reset it and start with new one"; +"invalid_storage_reset_button" = "Reset"; +"invalid_storage_reset_error" = "Couldn't remove storage. Please reinstall application"; +