From 32de56de7b8ff9f352ff4e969f1d21ccdacfdd53 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sat, 2 Oct 2021 13:14:03 +0300 Subject: [PATCH 1/8] Convert backupToInbox method from Promise to Future --- FlowCrypt.xcodeproj/project.pbxproj | 4 - .../BackupOptionsViewController.swift | 14 +- .../BackupSelectKeyViewController.swift | 14 +- .../SetupGenerateKeyViewController.swift | 154 +++++++++++++----- .../Backup Services/BackupService.swift | 65 ++++---- .../Backup Services/BackupServiceType.swift | 3 +- .../Mocks/BackupServiceMock.swift | 28 ---- 7 files changed, 165 insertions(+), 117 deletions(-) delete mode 100644 FlowCryptAppTests/Mocks/BackupServiceMock.swift diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 17812f76d..ef697353c 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -178,7 +178,6 @@ 9FC4112E2595EA8B001180A8 /* Gmail+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC4112D2595EA8B001180A8 /* Gmail+Search.swift */; }; 9FC411352595EA94001180A8 /* Imap+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC411342595EA94001180A8 /* Imap+Search.swift */; }; 9FC4114C25961CEA001180A8 /* MailServiceProviderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC4114B25961CEA001180A8 /* MailServiceProviderType.swift */; }; - 9FC4116526811861004C0A69 /* BackupServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4163EC266574CB00106194 /* BackupServiceMock.swift */; }; 9FC4116B2681186D004C0A69 /* KeyMethodsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA0157926565B7800CBBA05 /* KeyMethodsTest.swift */; }; 9FC41171268118A7004C0A69 /* PassPhraseStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC7EBA2266EB95300F3BF5D /* PassPhraseStorageTests.swift */; }; 9FC4117D268118AE004C0A69 /* PassPhraseStorageMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC7EBCF266EBE1D00F3BF5D /* PassPhraseStorageMock.swift */; }; @@ -519,7 +518,6 @@ 9F3EF33023B1785600FA0CEF /* MsgListViewConroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgListViewConroller.swift; sourceTree = ""; }; 9F4163B7265ED61C00106194 /* SetupInitialViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupInitialViewController.swift; sourceTree = ""; }; 9F4163E5266520B600106194 /* CommonNodesInputs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonNodesInputs.swift; sourceTree = ""; }; - 9F4163EC266574CB00106194 /* BackupServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupServiceMock.swift; sourceTree = ""; }; 9F416427266575DC00106194 /* BackupServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupServiceType.swift; sourceTree = ""; }; 9F41FA27253B75F4003B970D /* BackupSelectKeyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupSelectKeyViewController.swift; sourceTree = ""; }; 9F41FA2E253B7624003B970D /* BackupSelectKeyDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupSelectKeyDecorator.swift; sourceTree = ""; }; @@ -1320,7 +1318,6 @@ 9F6F3C6326ADFBDB005BD9C6 /* Mocks */ = { isa = PBXGroup; children = ( - 9F4163EC266574CB00106194 /* BackupServiceMock.swift */, 9F6F3C3B26ADFBC7005BD9C6 /* CoreComposeMessageMock.swift */, 9F6F3C6926ADFBEB005BD9C6 /* MessageGatewayMock.swift */, 9F6F3C7526ADFC37005BD9C6 /* KeyStorageMock.swift */, @@ -2499,7 +2496,6 @@ 9F976490267E11880058419D /* ImapHelperTest.swift in Sources */, 9F5F501D26F90AE100294FA2 /* OrganisationalRulesServiceMock.swift in Sources */, 9F5F503C26FA6C5E00294FA2 /* CurrentUserEmailMock.swift in Sources */, - 9FC4116526811861004C0A69 /* BackupServiceMock.swift in Sources */, 9FC413182683C492004C0A69 /* InMemoryPassPhraseStorageTest.swift in Sources */, 9F9764C5267E14AB0058419D /* GeneralConstantsTest.swift in Sources */, 9F976507267E165D0058419D /* ZBase32EncodingTests.swift in Sources */, diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift index f903a7189..32f0365cd 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift @@ -7,6 +7,7 @@ // import AsyncDisplayKit +import Combine import FlowCryptUI enum BackupOption: Int, CaseIterable, Equatable { @@ -34,6 +35,8 @@ final class BackupOptionsViewController: ASDKViewController { private let backupService: BackupServiceType private let userId: UserId + private var cancellable: AnyCancellable? + init( decorator: BackupOptionsViewDecoratorType = BackupOptionsViewDecorator(), backupService: BackupServiceType = BackupService(), @@ -103,14 +106,15 @@ extension BackupOptionsViewController { private func backupToInbox() { showSpinner() - backupService.backupToInbox(keys: backups, for: userId) - .then(on: .main) { [weak self] in + cancellable = backupService.backupToInbox(keys: backups, for: userId) + .subscribe(on: DispatchQueue.global()) + .receive(on: DispatchQueue.main) + .sinkFuture(receiveValue: { [weak self] _ in self?.hideSpinner() self?.navigationController?.popToRootViewController(animated: true) - } - .catch(on: .main) { [weak self] error in + }, receiveError: { [weak self] error in self?.handleCommon(error: error) - } + }) } private func backupAsFile() { diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift index b6c3f182d..654f809dd 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift @@ -7,6 +7,7 @@ // import AsyncDisplayKit +import Combine import FlowCryptUI import Foundation @@ -17,6 +18,8 @@ final class BackupSelectKeyViewController: ASDKViewController { private let selectedOption: BackupOption private let userId: UserId + private var cancellable: AnyCancellable? + init( decorator: BackupSelectKeyDecoratorType = BackupSelectKeyDecorator(), backupService: BackupServiceType = BackupService(), @@ -85,14 +88,15 @@ extension BackupSelectKeyViewController { .filter { $0.1 == true } .map(\.0) - backupService.backupToInbox(keys: backupsToSave, for: userId) - .then(on: .main) { [weak self] in + cancellable = backupService.backupToInbox(keys: backupsToSave, for: userId) + .subscribe(on: DispatchQueue.global()) + .receive(on: DispatchQueue.main) + .sinkFuture(receiveValue: { [weak self] _ in self?.hideSpinner() self?.navigationController?.popToRootViewController(animated: true) - } - .catch(on: .main) { [weak self] error in + }, receiveError: { [weak self] error in self?.handleCommon(error: error) - } + }) } private func backupAsFile() { diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index 85eccc497..02f34f32d 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -7,6 +7,7 @@ // import AsyncDisplayKit +import Combine import FlowCryptCommon import FlowCryptUI import Promises @@ -36,6 +37,7 @@ final class SetupGenerateKeyViewController: SetupCreatePassphraseAbstractViewCon private let attester: AttesterApiType private lazy var logger = Logger.nested(in: Self.self, with: .setup) + private var cancellable: AnyCancellable? init( user: UserId, @@ -75,51 +77,112 @@ final class SetupGenerateKeyViewController: SetupCreatePassphraseAbstractViewCon extension SetupGenerateKeyViewController { private func setupAccountWithGeneratedKey(with passPhrase: String) { - Promise { [weak self] in - guard let self = self else { return } - self.showSpinner() - - let userId = try self.getUserId() - - try awaitPromise(self.validateAndConfirmNewPassPhraseOrReject(passPhrase: passPhrase)) - - let encryptedPrv = try self.core.generateKey(passphrase: passPhrase, variant: .curve25519, userIds: [userId]) - - try awaitPromise(self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user)) - - let passPhrase = PassPhrase(value: passPhrase, fingerprints: encryptedPrv.key.fingerprints) - - self.keyStorage.addKeys(keyDetails: [encryptedPrv.key], source: .generated, for: self.user.email) - self.passPhraseService.savePassPhrase(with: passPhrase, inStorage: self.shouldStorePassPhrase) - - let updateKey = self.attester.updateKey( - email: userId.email, - pubkey: encryptedPrv.key.public, - token: self.storage.token - ) + showSpinner() + + cancellable = validateAndConfirm(passPhrase) + .subscribe(on: DispatchQueue.global()) + .flatMap(generateKey) + .flatMap(backupKey) + .flatMap(processKey) + .eraseToAnyPublisher() + .receive(on: DispatchQueue.main) + .sinkFuture { [weak self] _ in + self?.hideSpinner() + self?.moveToMainFlow() + } receiveError: { [weak self] error in + guard let self = self else { return } + self.hideSpinner() + + let isErrorHandled = self.handleCommon(error: error) + + if !isErrorHandled { + self.showAlert(error: error, message: "Could not finish setup, please try again") + } + } + } - try awaitPromise(self.alertAndSkipOnRejection( - updateKey, - fail: "Failed to submit Public Key") - ) - let testWelcome = self.attester.testWelcome(email: userId.email, pubkey: encryptedPrv.key.public) - try awaitPromise(self.alertAndSkipOnRejection( - testWelcome, - fail: "Failed to send you welcome email") - ) - } - .then(on: .main) { [weak self] in - self?.hideSpinner() - self?.moveToMainFlow() + private func validateAndConfirm(_ passPhrase: String) -> Future { + Future { [weak self] promise in + guard let self = self else { return } + do { + let userId = try self.getUserId() + try awaitPromise(self.validateAndConfirmNewPassPhraseOrReject(passPhrase: passPhrase)) + let input = GenerateKeyInput(passPhrase: passPhrase, userId: userId) + promise(.success(input)) + } catch { + promise(.failure(error)) + } } - .catch(on: .main) { [weak self] error in + } + + private func generateKey(_ input: GenerateKeyInput) -> Future { + Future { [weak self] promise in guard let self = self else { return } - self.hideSpinner() + do { + let result = try self.core.generateKey( + passphrase: input.passPhrase, + variant: .curve25519, + userIds: [input.userId] + ) + let input = ProcessKeyInput( + passPhrase: input.passPhrase, + userId: input.userId, + key: result.key + ) + promise(.success(input)) + } catch { + promise(.failure(error)) + } + } + } - let isErrorHandled = self.handleCommon(error: error) + private func backupKey(_ input: ProcessKeyInput) -> AnyPublisher { + backupService.backupToInbox(keys: [input.key], for: user) + .map({ _ in input }) + .eraseToAnyPublisher() + } - if !isErrorHandled { - self.showAlert(error: error, message: "Could not finish setup, please try again") + private func processKey(_ input: ProcessKeyInput) -> Future { + Future { [weak self] promise in + guard let self = self else { return } + do { + let passPhrase = PassPhrase( + value: input.passPhrase, + fingerprints: input.key.fingerprints + ) + + self.keyStorage.addKeys( + keyDetails: [input.key], + source: .generated, + for: self.user.email + ) + self.passPhraseService.savePassPhrase( + with: passPhrase, + inStorage: self.shouldStorePassPhrase + ) + + let updateKey: Promise = self.attester.updateKey( + email: input.userId.email, + pubkey: input.key.public, + token: self.storage.token + ) + try awaitPromise(self.alertAndSkipOnRejection( + updateKey, + fail: "Failed to submit Public Key") + ) + + let testWelcome: Promise = self.attester.testWelcome( + email: input.userId.email, + pubkey: input.key.public + ) + try awaitPromise(self.alertAndSkipOnRejection( + testWelcome, + fail: "Failed to send you welcome email") + ) + + promise(.success(())) + } catch { + promise(.failure(error)) } } } @@ -134,3 +197,14 @@ extension SetupGenerateKeyViewController { return UserId(email: email, name: name) } } + +private struct GenerateKeyInput { + var passPhrase: String + var userId: UserId +} + +private struct ProcessKeyInput { + var passPhrase: String + var userId: UserId + var key: KeyDetails +} diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index 659177536..8050f59fa 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -45,24 +45,42 @@ extension BackupService: BackupServiceType { } } - func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Promise { - Promise { [weak self] resolve, reject -> Void in - guard let self = self else { throw AppErr.nilSelf } + func backupToInbox(keys: [KeyDetails], for userId: UserId) -> AnyPublisher { + return createMessage(keys: keys, for: userId) + .flatMap(composeEmail) + .map(\.mimeEncoded) + .flatMap(messageSender.sendMail) + .eraseToAnyPublisher() + } - let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false) + func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) { + let file = keys.joinedPrivateKey + let activityViewController = UIActivityViewController( + activityItems: [file], + applicationActivities: nil + ) + viewController.present(activityViewController, animated: true) + } - guard isFullyEncryptedKeys else { - throw BackupServiceError.keyIsNotFullyEncrypted + private func createMessage(keys: [KeyDetails], for userId: UserId) -> Future { + Future { promise in + guard keys.map(\.isFullyDecrypted).contains(false) else { + promise(.failure(BackupServiceError.keyIsNotFullyEncrypted)) + return } + let filename = "flowcrypt-backup-\(userId.email.withoutSpecialCharacters).key" let privateKeyContext = keys .compactMap { $0 } .joinedPrivateKey - let privateKeyData = privateKeyContext.data().base64EncodedString() - - let filename = "flowcrypt-backup-\(userId.email.withoutSpecialCharacters).key" - let attachments = [SendableMsg.Attachment(name: filename, type: "text/plain", base64: privateKeyData)] + let attachments = [ + SendableMsg.Attachment( + name: filename, + type: "text/plain", + base64: privateKeyData + ) + ] let message = SendableMsg( text: "setup_backup_email".localized, to: [userId.toMime], @@ -75,33 +93,12 @@ extension BackupService: BackupServiceType { pubKeys: nil ) - self.core.composeEmail(msg: message, fmt: .plain, pubKeys: message.pubKeys) - .map(\.mimeEncoded) - .flatMap(self.messageSender.sendMail) - .sink( - receiveCompletion: { result in - switch result { - case .failure(let error): - reject(error) - case .finished: - break - } - }, - receiveValue: { - resolve(()) - } - ) - .store(in: &self.cancellable) + promise(.success(message)) } } - func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) { - let file = keys.joinedPrivateKey - let activityViewController = UIActivityViewController( - activityItems: [file], - applicationActivities: nil - ) - viewController.present(activityViewController, animated: true) + private func composeEmail(_ message: SendableMsg) -> Future { + core.composeEmail(msg: message, fmt: .plain, pubKeys: message.pubKeys) } } diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift index 76dbcdaa0..15f06049a 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift @@ -6,6 +6,7 @@ // Copyright © 2017-present FlowCrypt a. s. All rights reserved. // +import Combine import Foundation import Promises @@ -13,7 +14,7 @@ protocol BackupServiceType { /// get all existed backups func fetchBackupsFromInbox(for userId: UserId) -> Promise<[KeyDetails]> /// backup keys to user inbox - func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Promise + func backupToInbox(keys: [KeyDetails], for userId: UserId) -> AnyPublisher /// show activity sheet to save keys as file func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) } diff --git a/FlowCryptAppTests/Mocks/BackupServiceMock.swift b/FlowCryptAppTests/Mocks/BackupServiceMock.swift deleted file mode 100644 index bbc3f7c83..000000000 --- a/FlowCryptAppTests/Mocks/BackupServiceMock.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// BackupServiceMock.swift -// FlowCryptTests -// -// Created by Anton Kharchevskyi on 31.05.2021. -// Copyright © 2017-present FlowCrypt a. s. All rights reserved. -// - -import Foundation -import Promises -@testable import FlowCrypt - -final class BackupServiceMock: BackupServiceType { - var fetchBackupsResult: Result<[KeyDetails], Error> = .success([]) - func fetchBackupsFromInbox(for userId: UserId) -> Promise<[KeyDetails]> { - Promise<[KeyDetails]>.resolveAfter(with: fetchBackupsResult) - } - - var backupToInboxResult: Result = .success(()) - func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Promise { - Promise.resolveAfter(with: backupToInboxResult) - } - - var isBackupAsFile = false - func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) { - isBackupAsFile = true - } -} From 120e380c1747db8826c166c5459f5f65031af0ee Mon Sep 17 00:00:00 2001 From: Ivan Date: Sat, 2 Oct 2021 19:42:00 +0300 Subject: [PATCH 2/8] Convert backupToInbox method from Promise to Future --- .../BackupOptionsViewController.swift | 7 ++++++- .../BackupSelectKeyViewController.swift | 7 ++++++- .../Setup/SetupGenerateKeyViewController.swift | 3 ++- FlowCrypt/Extensions/CombineExtensions.swift | 11 +++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift index 32f0365cd..f7b2039d6 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift @@ -106,8 +106,9 @@ extension BackupOptionsViewController { private func backupToInbox() { showSpinner() - cancellable = backupService.backupToInbox(keys: backups, for: userId) + cancellable = Just(backups) .subscribe(on: DispatchQueue.global()) + .myFlatMap(backupToInbox) .receive(on: DispatchQueue.main) .sinkFuture(receiveValue: { [weak self] _ in self?.hideSpinner() @@ -120,6 +121,10 @@ extension BackupOptionsViewController { private func backupAsFile() { backupService.backupAsFile(keys: backups, for: self) } + + private func backupToInbox(_ input: [KeyDetails]) -> AnyPublisher { + backupService.backupToInbox(keys: input, for: userId) + } } // MARK: - ASTableDelegate, ASTableDataSource diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift index 654f809dd..3857e82d9 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift @@ -88,8 +88,9 @@ extension BackupSelectKeyViewController { .filter { $0.1 == true } .map(\.0) - cancellable = backupService.backupToInbox(keys: backupsToSave, for: userId) + cancellable = Just(backupsToSave) .subscribe(on: DispatchQueue.global()) + .myFlatMap(backupToInbox) .receive(on: DispatchQueue.main) .sinkFuture(receiveValue: { [weak self] _ in self?.hideSpinner() @@ -102,6 +103,10 @@ extension BackupSelectKeyViewController { private func backupAsFile() { backupService.backupAsFile(keys: backupsContext.map(\.0), for: self) } + + private func backupToInbox(_ input: [KeyDetails]) -> AnyPublisher { + backupService.backupToInbox(keys: input, for: userId) + } } // MARK: - ASTableDelegate, ASTableDataSource diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index 02f34f32d..9618b9791 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -79,8 +79,9 @@ extension SetupGenerateKeyViewController { private func setupAccountWithGeneratedKey(with passPhrase: String) { showSpinner() - cancellable = validateAndConfirm(passPhrase) + cancellable = Just(passPhrase) .subscribe(on: DispatchQueue.global()) + .myFlatMap(validateAndConfirm) .flatMap(generateKey) .flatMap(backupKey) .flatMap(processKey) diff --git a/FlowCrypt/Extensions/CombineExtensions.swift b/FlowCrypt/Extensions/CombineExtensions.swift index 87e56f178..b55d2300a 100644 --- a/FlowCrypt/Extensions/CombineExtensions.swift +++ b/FlowCrypt/Extensions/CombineExtensions.swift @@ -25,3 +25,14 @@ extension Publisher { }) } } + +extension Publishers.SubscribeOn { + // swiftlint:disable line_length + func myFlatMap

(_ transform: @escaping (Self.Output) -> P) -> Publishers.FlatMap> where P: Publisher { + Publishers.FlatMap>( + upstream: Publishers.SetFailureType(upstream: self), + maxPublishers: .unlimited, + transform: transform + ) + } +} From 9e1edbc131c95f66593a0a556aef7c0bb9d56826 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sat, 2 Oct 2021 23:51:27 +0300 Subject: [PATCH 3/8] Convert backupToInbox method from Promise to Future --- .../BackupOptionsViewController.swift | 3 ++- .../BackupSelectKeyViewController.swift | 3 ++- .../Setup/SetupGenerateKeyViewController.swift | 3 ++- FlowCrypt/Extensions/CombineExtensions.swift | 11 ----------- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift index f7b2039d6..1a4569920 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift @@ -107,8 +107,9 @@ extension BackupOptionsViewController { private func backupToInbox() { showSpinner() cancellable = Just(backups) + .setFailureType(to: Error.self) .subscribe(on: DispatchQueue.global()) - .myFlatMap(backupToInbox) + .flatMap(backupToInbox) .receive(on: DispatchQueue.main) .sinkFuture(receiveValue: { [weak self] _ in self?.hideSpinner() diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift index 3857e82d9..ab70586df 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift @@ -89,8 +89,9 @@ extension BackupSelectKeyViewController { .map(\.0) cancellable = Just(backupsToSave) + .setFailureType(to: Error.self) .subscribe(on: DispatchQueue.global()) - .myFlatMap(backupToInbox) + .flatMap(backupToInbox) .receive(on: DispatchQueue.main) .sinkFuture(receiveValue: { [weak self] _ in self?.hideSpinner() diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index 9618b9791..1397b9faf 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -80,8 +80,9 @@ extension SetupGenerateKeyViewController { showSpinner() cancellable = Just(passPhrase) + .setFailureType(to: Error.self) .subscribe(on: DispatchQueue.global()) - .myFlatMap(validateAndConfirm) + .flatMap(validateAndConfirm) .flatMap(generateKey) .flatMap(backupKey) .flatMap(processKey) diff --git a/FlowCrypt/Extensions/CombineExtensions.swift b/FlowCrypt/Extensions/CombineExtensions.swift index b55d2300a..87e56f178 100644 --- a/FlowCrypt/Extensions/CombineExtensions.swift +++ b/FlowCrypt/Extensions/CombineExtensions.swift @@ -25,14 +25,3 @@ extension Publisher { }) } } - -extension Publishers.SubscribeOn { - // swiftlint:disable line_length - func myFlatMap

(_ transform: @escaping (Self.Output) -> P) -> Publishers.FlatMap> where P: Publisher { - Publishers.FlatMap>( - upstream: Publishers.SetFailureType(upstream: self), - maxPublishers: .unlimited, - transform: transform - ) - } -} From 8b44b42ed86118104b7c06c6f93b79cd987a5c38 Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 4 Oct 2021 20:17:30 +0300 Subject: [PATCH 4/8] Convert backupToInbox method from Promise to Future --- .../BackupOptionsViewController.swift | 13 +- .../BackupSelectKeyViewController.swift | 11 +- .../SetupGenerateKeyViewController.swift | 158 +++++------------- FlowCrypt/Extensions/CombineExtensions.swift | 11 ++ .../Backup Services/BackupService.swift | 96 ++++++----- .../Backup Services/BackupServiceType.swift | 2 +- 6 files changed, 112 insertions(+), 179 deletions(-) diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift index 1a4569920..1497c508a 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift @@ -7,8 +7,8 @@ // import AsyncDisplayKit -import Combine import FlowCryptUI +import Combine enum BackupOption: Int, CaseIterable, Equatable { case email, download @@ -106,12 +106,9 @@ extension BackupOptionsViewController { private func backupToInbox() { showSpinner() - cancellable = Just(backups) - .setFailureType(to: Error.self) - .subscribe(on: DispatchQueue.global()) - .flatMap(backupToInbox) + cancellable = backupService.backupToInbox(keys: backups, for: userId) .receive(on: DispatchQueue.main) - .sinkFuture(receiveValue: { [weak self] _ in + .sinkFuture(receiveValue: { [weak self] in self?.hideSpinner() self?.navigationController?.popToRootViewController(animated: true) }, receiveError: { [weak self] error in @@ -122,10 +119,6 @@ extension BackupOptionsViewController { private func backupAsFile() { backupService.backupAsFile(keys: backups, for: self) } - - private func backupToInbox(_ input: [KeyDetails]) -> AnyPublisher { - backupService.backupToInbox(keys: input, for: userId) - } } // MARK: - ASTableDelegate, ASTableDataSource diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift index ab70586df..5afe360c7 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift @@ -88,12 +88,9 @@ extension BackupSelectKeyViewController { .filter { $0.1 == true } .map(\.0) - cancellable = Just(backupsToSave) - .setFailureType(to: Error.self) - .subscribe(on: DispatchQueue.global()) - .flatMap(backupToInbox) + cancellable = backupService.backupToInbox(keys: backupsToSave, for: userId) .receive(on: DispatchQueue.main) - .sinkFuture(receiveValue: { [weak self] _ in + .sinkFuture(receiveValue: { [weak self] in self?.hideSpinner() self?.navigationController?.popToRootViewController(animated: true) }, receiveError: { [weak self] error in @@ -104,10 +101,6 @@ extension BackupSelectKeyViewController { private func backupAsFile() { backupService.backupAsFile(keys: backupsContext.map(\.0), for: self) } - - private func backupToInbox(_ input: [KeyDetails]) -> AnyPublisher { - backupService.backupToInbox(keys: input, for: userId) - } } // MARK: - ASTableDelegate, ASTableDataSource diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index becc5fc64..394808755 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -7,7 +7,6 @@ // import AsyncDisplayKit -import Combine import FlowCryptCommon import FlowCryptUI import Promises @@ -37,7 +36,6 @@ final class SetupGenerateKeyViewController: SetupCreatePassphraseAbstractViewCon private let attester: AttesterApiType private lazy var logger = Logger.nested(in: Self.self, with: .setup) - private var cancellable: AnyCancellable? init( user: UserId, @@ -77,113 +75,56 @@ final class SetupGenerateKeyViewController: SetupCreatePassphraseAbstractViewCon extension SetupGenerateKeyViewController { private func setupAccountWithGeneratedKey(with passPhrase: String) { - showSpinner() - - cancellable = Just(passPhrase) - .setFailureType(to: Error.self) - .subscribe(on: DispatchQueue.global()) - .flatMap(validateAndConfirm) - .flatMap(generateKey) - .flatMap(backupKey) - .flatMap(processKey) - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .sinkFuture { [weak self] _ in - self?.hideSpinner() - self?.moveToMainFlow() - } receiveError: { [weak self] error in - guard let self = self else { return } - self.hideSpinner() - - let isErrorHandled = self.handleCommon(error: error) - - if !isErrorHandled { - self.showAlert(error: error, message: "Could not finish setup, please try again") - } - } - } - - private func validateAndConfirm(_ passPhrase: String) -> Future { - Future { [weak self] promise in + Promise { [weak self] in guard let self = self else { return } - do { - let userId = try self.getUserId() - try awaitPromise(self.validateAndConfirmNewPassPhraseOrReject(passPhrase: passPhrase)) - let input = GenerateKeyInput(passPhrase: passPhrase, userId: userId) - promise(.success(input)) - } catch { - promise(.failure(error)) - } - } - } + self.showSpinner() - private func generateKey(_ input: GenerateKeyInput) -> Future { - Future { [weak self] promise in - guard let self = self else { return } - do { - let result = try self.core.generateKey( - passphrase: input.passPhrase, - variant: .curve25519, - userIds: [input.userId] - ) - let input = ProcessKeyInput( - passPhrase: input.passPhrase, - userId: input.userId, - key: result.key - ) - promise(.success(input)) - } catch { - promise(.failure(error)) + let userId = try self.getUserId() + + try awaitPromise(self.validateAndConfirmNewPassPhraseOrReject(passPhrase: passPhrase)) + + let encryptedPrv = try self.core.generateKey(passphrase: passPhrase, variant: .curve25519, userIds: [userId]) + + self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user).wait() + + self.keyStorage.addKeys(keyDetails: [encryptedPrv.key], + passPhrase: self.shouldStorePassPhrase ? passPhrase: nil, + source: .generated, + for: self.user.email) + + if !self.shouldStorePassPhrase { + let passPhrase = PassPhrase(value: passPhrase, fingerprints: encryptedPrv.key.fingerprints) + self.passPhraseService.savePassPhrase(with: passPhrase, inStorage: false) } + + let updateKey = self.attester.updateKey( + email: userId.email, + pubkey: encryptedPrv.key.public, + token: self.storage.token + ) + + try awaitPromise(self.alertAndSkipOnRejection( + updateKey, + fail: "Failed to submit Public Key") + ) + let testWelcome = self.attester.testWelcome(email: userId.email, pubkey: encryptedPrv.key.public) + try awaitPromise(self.alertAndSkipOnRejection( + testWelcome, + fail: "Failed to send you welcome email") + ) } - } + .then(on: .main) { [weak self] in + self?.hideSpinner() + self?.moveToMainFlow() + } + .catch(on: .main) { [weak self] error in + guard let self = self else { return } + self.hideSpinner() - private func backupKey(_ input: ProcessKeyInput) -> AnyPublisher { - backupService.backupToInbox(keys: [input.key], for: user) - .map({ _ in input }) - .eraseToAnyPublisher() - } + let isErrorHandled = self.handleCommon(error: error) - private func processKey(_ input: ProcessKeyInput) -> Future { - Future { [weak self] promise in - guard let self = self else { return } - do { - self.keyStorage.addKeys( - keyDetails: [input.key], - passPhrase: self.shouldStorePassPhrase ? input.passPhrase: nil, - source: .generated, - for: self.user.email - ) - if !self.shouldStorePassPhrase { - let passPhrase = PassPhrase( - value: input.passPhrase, - fingerprints: input.key.fingerprints - ) - self.passPhraseService.savePassPhrase(with: passPhrase, inStorage: false) - } - - let updateKey: Promise = self.attester.updateKey( - email: input.userId.email, - pubkey: input.key.public, - token: self.storage.token - ) - try awaitPromise(self.alertAndSkipOnRejection( - updateKey, - fail: "Failed to submit Public Key") - ) - - let testWelcome: Promise = self.attester.testWelcome( - email: input.userId.email, - pubkey: input.key.public - ) - try awaitPromise(self.alertAndSkipOnRejection( - testWelcome, - fail: "Failed to send you welcome email") - ) - - promise(.success(())) - } catch { - promise(.failure(error)) + if !isErrorHandled { + self.showAlert(error: error, message: "Could not finish setup, please try again") } } } @@ -198,14 +139,3 @@ extension SetupGenerateKeyViewController { return UserId(email: email, name: name) } } - -private struct GenerateKeyInput { - var passPhrase: String - var userId: UserId -} - -private struct ProcessKeyInput { - var passPhrase: String - var userId: UserId - var key: KeyDetails -} diff --git a/FlowCrypt/Extensions/CombineExtensions.swift b/FlowCrypt/Extensions/CombineExtensions.swift index 87e56f178..fe832f139 100644 --- a/FlowCrypt/Extensions/CombineExtensions.swift +++ b/FlowCrypt/Extensions/CombineExtensions.swift @@ -8,6 +8,7 @@ import Combine import Foundation +import RealmSwift extension Publisher { /// Attaches a subscriber with closure-based behaviour which will emit value and error @@ -24,4 +25,14 @@ extension Publisher { receiveValue(value) }) } + + func wait() { + let semaphore = DispatchSemaphore(value: 0) + _ = sinkFuture { _ in + semaphore.signal() + } receiveError: { _ in + semaphore.signal() + } + semaphore.wait() + } } diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index 8050f59fa..cabc84a79 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -45,12 +45,57 @@ extension BackupService: BackupServiceType { } } - func backupToInbox(keys: [KeyDetails], for userId: UserId) -> AnyPublisher { - return createMessage(keys: keys, for: userId) - .flatMap(composeEmail) - .map(\.mimeEncoded) - .flatMap(messageSender.sendMail) - .eraseToAnyPublisher() + func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Future { + Future { [weak self] promise in + DispatchQueue.global().async { + guard let self = self else { return } + + let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false) + + guard isFullyEncryptedKeys else { + promise(.failure(BackupServiceError.keyIsNotFullyEncrypted)) + return + } + + let privateKeyContext = keys + .compactMap { $0 } + .joinedPrivateKey + + let privateKeyData = privateKeyContext.data().base64EncodedString() + + let filename = "flowcrypt-backup-\(userId.email.withoutSpecialCharacters).key" + let attachments = [SendableMsg.Attachment(name: filename, type: "text/plain", base64: privateKeyData)] + let message = SendableMsg( + text: "setup_backup_email".localized, + to: [userId.toMime], + cc: [], + bcc: [], + from: userId.toMime, + subject: "Your FlowCrypt Backup", + replyToMimeMsg: nil, + atts: attachments, + pubKeys: nil + ) + + self.core.composeEmail(msg: message, fmt: .plain, pubKeys: message.pubKeys) + .map(\.mimeEncoded) + .flatMap(self.messageSender.sendMail) + .sink( + receiveCompletion: { result in + switch result { + case .failure(let error): + promise(.failure(error)) + case .finished: + break + } + }, + receiveValue: { + promise(.success(())) + } + ) + .store(in: &self.cancellable) + } + } } func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) { @@ -61,45 +106,6 @@ extension BackupService: BackupServiceType { ) viewController.present(activityViewController, animated: true) } - - private func createMessage(keys: [KeyDetails], for userId: UserId) -> Future { - Future { promise in - guard keys.map(\.isFullyDecrypted).contains(false) else { - promise(.failure(BackupServiceError.keyIsNotFullyEncrypted)) - return - } - - let filename = "flowcrypt-backup-\(userId.email.withoutSpecialCharacters).key" - let privateKeyContext = keys - .compactMap { $0 } - .joinedPrivateKey - let privateKeyData = privateKeyContext.data().base64EncodedString() - let attachments = [ - SendableMsg.Attachment( - name: filename, - type: "text/plain", - base64: privateKeyData - ) - ] - let message = SendableMsg( - text: "setup_backup_email".localized, - to: [userId.toMime], - cc: [], - bcc: [], - from: userId.toMime, - subject: "Your FlowCrypt Backup", - replyToMimeMsg: nil, - atts: attachments, - pubKeys: nil - ) - - promise(.success(message)) - } - } - - private func composeEmail(_ message: SendableMsg) -> Future { - core.composeEmail(msg: message, fmt: .plain, pubKeys: message.pubKeys) - } } // MARK: - Helpers diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift index 15f06049a..372ed21ad 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift @@ -14,7 +14,7 @@ protocol BackupServiceType { /// get all existed backups func fetchBackupsFromInbox(for userId: UserId) -> Promise<[KeyDetails]> /// backup keys to user inbox - func backupToInbox(keys: [KeyDetails], for userId: UserId) -> AnyPublisher + func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Future /// show activity sheet to save keys as file func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) } From 9dd2157e409a19643e467506f4ceb0b892f980fe Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 12 Oct 2021 18:49:19 +0300 Subject: [PATCH 5/8] Convert backupToInbox method from Promise to Future --- FlowCrypt.xcodeproj/project.pbxproj | 4 - .../Compose/ComposeViewController.swift | 36 +++---- .../BackupOptionsViewController.swift | 20 ++-- .../BackupSelectKeyViewController.swift | 19 ++-- .../SetupGenerateKeyViewController.swift | 7 +- FlowCrypt/Core/Core.swift | 38 +++----- FlowCrypt/Extensions/CombineExtensions.swift | 10 -- .../Message Gateway/GmailService+send.swift | 14 +-- .../Message Gateway/Imap+send.swift | 8 +- .../Message Gateway/MessageGateway.swift | 2 +- .../Backup Services/BackupService.swift | 71 +++++--------- .../Backup Services/BackupServiceType.swift | 2 +- .../ComposeMessageService.swift | 29 +++--- .../Core/FlowCryptCoreTests.swift | 94 +++++++------------ .../Mocks/CoreComposeMessageMock.swift | 9 +- .../Mocks/MessageGatewayMock.swift | 6 +- 16 files changed, 153 insertions(+), 216 deletions(-) diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index c74d0904e..99fd53fe8 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -99,7 +99,6 @@ 9F2AC5B1267BDED100F6149B /* GmailSearchExpressionGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2AC5B0267BDED100F6149B /* GmailSearchExpressionGenerator.swift */; }; 9F2F206826AEEAA60044E144 /* CombineTestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2F206726AEEAA60044E144 /* CombineTestExtension.swift */; }; 9F2F207326AEECFB0044E144 /* PromiseTestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC411892681191D004C0A69 /* PromiseTestExtension.swift */; }; - 9F2F212B26B305420044E144 /* test-ci-secrets.json in Resources */ = {isa = PBXBuildFile; fileRef = 9F2F212A26B305420044E144 /* test-ci-secrets.json */; }; 9F2F217326B3269D0044E144 /* CombineExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2F217226B3269D0044E144 /* CombineExtensions.swift */; }; 9F31AB8C23298B3F00CF87EA /* Imap+retry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F31AB8B23298B3F00CF87EA /* Imap+retry.swift */; }; 9F31AB8E23298BCF00CF87EA /* Imap+folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F31AB8D23298BCF00CF87EA /* Imap+folders.swift */; }; @@ -1307,8 +1306,6 @@ 9F6F3C6326ADFBDB005BD9C6 /* Mocks */ = { isa = PBXGroup; children = ( - 9F6F3C3B26ADFBC7005BD9C6 /* CoreComposeMessageMock.swift */, - 9F4163EC266574CB00106194 /* BackupServiceMock.swift */, 9F6F3C6926ADFBEB005BD9C6 /* MessageGatewayMock.swift */, 9F6F3C3B26ADFBC7005BD9C6 /* CoreComposeMessageMock.swift */, 9F6F3C7526ADFC37005BD9C6 /* KeyStorageMock.swift */, @@ -2220,7 +2217,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9F2F212B26B305420044E144 /* test-ci-secrets.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index 3cc02014a..0bf62598d 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -206,26 +206,30 @@ extension ComposeViewController { showSpinner("sending_title".localized) navigationItem.rightBarButtonItem?.isEnabled = false - composeMessageService.validateMessage( + let result = composeMessageService.validateMessage( input: input, contextToSend: contextToSend, email: email ) - .publisher - .flatMap(encryptAndSend) - .receive(on: DispatchQueue.main) - .sinkFuture( - receiveValue: { [weak self] in - self?.handleSuccessfullySentMessage() - }, - receiveError: { [weak self] error in - self?.handle(error: error) - }) - .store(in: &cancellable) - } - - private func encryptAndSend(_ message: SendableMsg) -> AnyPublisher { - composeMessageService.encryptAndSend(message: message, threadId: input.threadId) + switch result { + case .success(let message): + encryptAndSend(message) + case .failure(let error): + handle(error: error) + } + } + + private func encryptAndSend(_ message: SendableMsg) { + Task { + do { + try await composeMessageService.encryptAndSend(message: message, threadId: input.threadId) + handleSuccessfullySentMessage() + } catch { + if let error = error as? ComposeMessageError { + handle(error: error) + } + } + } } private func handle(error: ComposeMessageError) { diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift index 7611b5ad4..b26c44afa 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Option Scene/BackupOptionsViewController.swift @@ -35,8 +35,6 @@ final class BackupOptionsViewController: ASDKViewController { private let backupService: BackupServiceType private let userId: UserId - private var cancellable: AnyCancellable? - init( decorator: BackupOptionsViewDecoratorType = BackupOptionsViewDecorator(), backupService: BackupServiceType = BackupService(), @@ -106,14 +104,16 @@ extension BackupOptionsViewController { private func backupToInbox() { showSpinner() - cancellable = backupService.backupToInbox(keys: backups, for: userId) - .receive(on: DispatchQueue.main) - .sinkFuture(receiveValue: { [weak self] in - self?.hideSpinner() - self?.navigationController?.popToRootViewController(animated: true) - }, receiveError: { [weak self] error in - self?.handleCommon(error: error) - }) + + Task { + do { + try await backupService.backupToInbox(keys: backups, for: userId) + hideSpinner() + navigationController?.popToRootViewController(animated: true) + } catch { + handleCommon(error: error) + } + } } private func backupAsFile() { diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift index 5afe360c7..aa15bc3b0 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift @@ -18,8 +18,6 @@ final class BackupSelectKeyViewController: ASDKViewController { private let selectedOption: BackupOption private let userId: UserId - private var cancellable: AnyCancellable? - init( decorator: BackupSelectKeyDecoratorType = BackupSelectKeyDecorator(), backupService: BackupServiceType = BackupService(), @@ -88,14 +86,15 @@ extension BackupSelectKeyViewController { .filter { $0.1 == true } .map(\.0) - cancellable = backupService.backupToInbox(keys: backupsToSave, for: userId) - .receive(on: DispatchQueue.main) - .sinkFuture(receiveValue: { [weak self] in - self?.hideSpinner() - self?.navigationController?.popToRootViewController(animated: true) - }, receiveError: { [weak self] error in - self?.handleCommon(error: error) - }) + Task { + do { + try await backupService.backupToInbox(keys: backupsToSave, for: userId) + hideSpinner() + navigationController?.popToRootViewController(animated: true) + } catch { + handleCommon(error: error) + } + } } private func backupAsFile() { diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index 47e8aa22a..a895c1f80 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -85,7 +85,12 @@ extension SetupGenerateKeyViewController { let encryptedPrv = try self.core.generateKey(passphrase: passPhrase, variant: .curve25519, userIds: [userId]) - self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user).wait() + let semaphore = DispatchSemaphore(value: 0) + Task { + try await self.backupService.backupToInbox(keys: [encryptedPrv.key], for: self.user) + semaphore.signal() + } + semaphore.wait() self.keyStorage.addKeys(keyDetails: [encryptedPrv.key], passPhrase: self.storageMethod == .persistent ? passPhrase: nil, diff --git a/FlowCrypt/Core/Core.swift b/FlowCrypt/Core/Core.swift index 08425136d..50f6ed3fa 100644 --- a/FlowCrypt/Core/Core.swift +++ b/FlowCrypt/Core/Core.swift @@ -151,30 +151,20 @@ final class Core: KeyDecrypter, CoreComposeMessageType { ) } - func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) -> Future { - Future { [weak self] promise in - guard let self = self else { return } - self.queue.async { - do { - let r = try self.call("composeEmail", jsonDict: [ - "text": msg.text, - "to": msg.to, - "cc": msg.cc, - "bcc": msg.bcc, - "from": msg.from, - "subject": msg.subject, - "replyToMimeMsg": msg.replyToMimeMsg, - "atts": msg.atts.map { att in ["name": att.name, "type": att.type, "base64": att.base64] }, - "format": fmt.rawValue, - "pubKeys": pubKeys, - ], data: nil) - // this call returned no useful json data, only bytes - promise(.success(CoreRes.ComposeEmail(mimeEncoded: r.data))) - } catch { - promise(.failure(error)) - } - } - } + func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) async throws -> CoreRes.ComposeEmail { + let r = try call("composeEmail", jsonDict: [ + "text": msg.text, + "to": msg.to, + "cc": msg.cc, + "bcc": msg.bcc, + "from": msg.from, + "subject": msg.subject, + "replyToMimeMsg": msg.replyToMimeMsg, + "atts": msg.atts.map { att in ["name": att.name, "type": att.type, "base64": att.base64] }, + "format": fmt.rawValue, + "pubKeys": pubKeys, + ], data: nil) + return CoreRes.ComposeEmail(mimeEncoded: r.data) } func zxcvbnStrengthBar(passPhrase: String) throws -> CoreRes.ZxcvbnStrengthBar { diff --git a/FlowCrypt/Extensions/CombineExtensions.swift b/FlowCrypt/Extensions/CombineExtensions.swift index fe832f139..f30882d78 100644 --- a/FlowCrypt/Extensions/CombineExtensions.swift +++ b/FlowCrypt/Extensions/CombineExtensions.swift @@ -25,14 +25,4 @@ extension Publisher { receiveValue(value) }) } - - func wait() { - let semaphore = DispatchSemaphore(value: 0) - _ = sinkFuture { _ in - semaphore.signal() - } receiveError: { _ in - semaphore.signal() - } - semaphore.wait() - } } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift b/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift index 98bda9aa5..fc1061780 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift @@ -11,10 +11,11 @@ import Foundation import GoogleAPIClientForREST_Gmail extension GmailService: MessageGateway { - func sendMail(input: MessageGatewayInput) -> Future { - Future { promise in + func sendMail(input: MessageGatewayInput) async throws { + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in guard let raw = GTLREncodeBase64(input.mime) else { - return promise(.failure(GmailServiceError.messageEncode)) + continuation.resume(throwing: GmailServiceError.messageEncode) + return } let gtlMessage = GTLRGmail_Message() @@ -27,11 +28,12 @@ extension GmailService: MessageGateway { uploadParameters: nil ) - self.gmailService.executeQuery(querySend) { _, _, error in + gmailService.executeQuery(querySend) { _, _, error in if let error = error { - return promise(.failure(GmailServiceError.providerError(error))) + continuation.resume(throwing: GmailServiceError.providerError(error)) + } else { + continuation.resume(returning: ()) } - promise(.success(())) } } } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift b/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift index d11e46398..0464c2573 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift @@ -6,14 +6,14 @@ import Combine import Foundation extension Imap: MessageGateway { - func sendMail(input: MessageGatewayInput) -> Future { - Future { [smtpSess] promise in + func sendMail(input: MessageGatewayInput) async throws { + try await withCheckedThrowingContinuation { [smtpSess] (continuation: CheckedContinuation) in smtpSess?.sendOperation(with: input.mime) .start { error in if let error = error { - promise(.failure(error)) + continuation.resume(throwing: error) } else { - promise(.success(())) + continuation.resume(returning: ()) } } } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Gateway/MessageGateway.swift b/FlowCrypt/Functionality/Mail Provider/Message Gateway/MessageGateway.swift index 26e1e4212..764de31e8 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/MessageGateway.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/MessageGateway.swift @@ -15,5 +15,5 @@ struct MessageGatewayInput { } protocol MessageGateway { - func sendMail(input: MessageGatewayInput) -> Future + func sendMail(input: MessageGatewayInput) async throws } diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index c3a2d9871..25ae7b4de 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -45,53 +45,34 @@ extension BackupService: BackupServiceType { } } - func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Promise { - Promise { [weak self] resolve, reject -> Void in - guard let self = self else { throw AppErr.nilSelf } - - let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false) - - guard isFullyEncryptedKeys else { - throw BackupServiceError.keyIsNotFullyEncrypted - } + func backupToInbox(keys: [KeyDetails], for userId: UserId) async throws { + let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false) - let privateKeyContext = keys - .compactMap { $0 } - .joinedPrivateKey - - let privateKeyData = privateKeyContext.data().base64EncodedString() - - let filename = "flowcrypt-backup-\(userId.email.withoutSpecialCharacters).key" - let attachments = [SendableMsg.Attachment(name: filename, type: "text/plain", base64: privateKeyData)] - let message = SendableMsg( - text: "setup_backup_email".localized, - to: [userId.toMime], - cc: [], - bcc: [], - from: userId.toMime, - subject: "Your FlowCrypt Backup", - replyToMimeMsg: nil, - atts: attachments, - pubKeys: nil) - - self.core.composeEmail(msg: message, fmt: .plain, pubKeys: message.pubKeys) - .map({ MessageGatewayInput(mime: $0.mimeEncoded, threadId: nil) }) - .flatMap(self.messageSender.sendMail) - .sink( - receiveCompletion: { result in - switch result { - case .failure(let error): - reject(error) - case .finished: - break - } - }, - receiveValue: { - resolve(()) - } - ) - .store(in: &self.cancellable) + guard isFullyEncryptedKeys else { + throw BackupServiceError.keyIsNotFullyEncrypted } + + let privateKeyContext = keys + .compactMap { $0 } + .joinedPrivateKey + + let privateKeyData = privateKeyContext.data().base64EncodedString() + + let filename = "flowcrypt-backup-\(userId.email.withoutSpecialCharacters).key" + let attachments = [SendableMsg.Attachment(name: filename, type: "text/plain", base64: privateKeyData)] + let message = SendableMsg( + text: "setup_backup_email".localized, + to: [userId.toMime], + cc: [], + bcc: [], + from: userId.toMime, + subject: "Your FlowCrypt Backup", + replyToMimeMsg: nil, + atts: attachments, + pubKeys: nil) + + let t = try await core.composeEmail(msg: message, fmt: .plain, pubKeys: message.pubKeys) + try await messageSender.sendMail(input: MessageGatewayInput(mime: t.mimeEncoded, threadId: nil)) } func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) { diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift index 2a307d9ec..bef7f4bb0 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupServiceType.swift @@ -13,7 +13,7 @@ protocol BackupServiceType { /// get all existed backups func fetchBackupsFromInbox(for userId: UserId) -> Promise<[KeyDetails]> /// backup keys to user inbox - func backupToInbox(keys: [KeyDetails], for userId: UserId) -> Promise + func backupToInbox(keys: [KeyDetails], for userId: UserId) async throws /// show activity sheet to save keys as file func backupAsFile(keys: [KeyDetails], for viewController: UIViewController) } diff --git a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift index 6923e4a2d..864064a4f 100644 --- a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift +++ b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift @@ -23,7 +23,7 @@ struct ComposeMessageRecipient { } protocol CoreComposeMessageType { - func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) -> Future + func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) async throws -> CoreRes.ComposeEmail } final class ComposeMessageService { @@ -126,20 +126,17 @@ final class ComposeMessageService { } // MARK: - Encrypt and Send - func encryptAndSend(message: SendableMsg, threadId: String?) -> AnyPublisher { - return encryptMessage(with: message, threadId: threadId) - .flatMap(messageGateway.sendMail) - .mapError { ComposeMessageError.gatewayError($0) } - .eraseToAnyPublisher() - } - - private func encryptMessage(with msg: SendableMsg, threadId: String?) -> AnyPublisher { - return core.composeEmail( - msg: msg, - fmt: MsgFmt.encryptInline, - pubKeys: msg.pubKeys - ) - .map({ MessageGatewayInput(mime: $0.mimeEncoded, threadId: threadId) }) - .eraseToAnyPublisher() + func encryptAndSend(message: SendableMsg, threadId: String?) async throws { + do { + let r = try await core.composeEmail( + msg: message, + fmt: MsgFmt.encryptInline, + pubKeys: message.pubKeys + ) + + try await messageGateway.sendMail(input: MessageGatewayInput(mime: r.mimeEncoded, threadId: threadId)) + } catch { + throw ComposeMessageError.gatewayError(error) + } } } diff --git a/FlowCryptAppTests/Core/FlowCryptCoreTests.swift b/FlowCryptAppTests/Core/FlowCryptCoreTests.swift index 9bf7a0b5a..227c5e9d1 100644 --- a/FlowCryptAppTests/Core/FlowCryptCoreTests.swift +++ b/FlowCryptAppTests/Core/FlowCryptCoreTests.swift @@ -93,58 +93,55 @@ class FlowCryptCoreTests: XCTestCase { XCTAssertThrowsError(try core.decryptKey(armoredPrv: TestData.k0.prv, passphrase: "wrong")) } - func testComposeEmailPlain() throws { - let msg = SendableMsg(text: "this is the message", to: ["email@hello.com"], cc: [], bcc: [], from: "sender@hello.com", subject: "subj", replyToMimeMsg: nil, atts: [], pubKeys: nil) - let expectation = XCTestExpectation() - - var mime: String = "" - core.composeEmail(msg: msg, fmt: .plain, pubKeys: nil) - .sinkFuture( - receiveValue: { composeEmailRes in - mime = String(data: composeEmailRes.mimeEncoded, encoding: .utf8)! - expectation.fulfill() - }, receiveError: {_ in } - ) - .store(in: &cancellable) - wait(for: [expectation], timeout: 3) + func testComposeEmailPlain() async throws { + let msg = SendableMsg( + text: "this is the message", + to: ["email@hello.com"], + cc: [], + bcc: [], + from: "sender@hello.com", + subject: "subj", + replyToMimeMsg: nil, + atts: [], + pubKeys: nil + ) + let r = try await core.composeEmail(msg: msg, fmt: .plain, pubKeys: nil) + let mime = String(data: r.mimeEncoded, encoding: .utf8)! XCTAssertNil(mime.range(of: "-----BEGIN PGP MESSAGE-----")) // not encrypted XCTAssertNotNil(mime.range(of: msg.text)) // plain text visible XCTAssertNotNil(mime.range(of: "Subject: \(msg.subject)")) // has mime Subject header XCTAssertNil(mime.range(of: "In-Reply-To")) // Not a reply } - func testComposeEmailEncryptInline() throws { - let msg = SendableMsg(text: "this is the message", to: ["email@hello.com"], cc: [], bcc: [], from: "sender@hello.com", subject: "subj", replyToMimeMsg: nil, atts: [], pubKeys: nil) - let expectation = XCTestExpectation() - - var mime: String = "" - core.composeEmail(msg: msg, fmt: .encryptInline, pubKeys: [TestData.k0.pub, TestData.k1.pub]) - .sinkFuture( - receiveValue: { composeEmailRes in - mime = String(data: composeEmailRes.mimeEncoded, encoding: .utf8)! - expectation.fulfill() - }, receiveError: {_ in } - ) - .store(in: &cancellable) - wait(for: [expectation], timeout: 3) + func testComposeEmailEncryptInline() async throws { + let msg = SendableMsg( + text: "this is the message", + to: ["email@hello.com"], + cc: [], + bcc: [], + from: "sender@hello.com", + subject: "subj", + replyToMimeMsg: nil, + atts: [], + pubKeys: nil + ) + let r = try await self.core.composeEmail(msg: msg, fmt: .encryptInline, pubKeys: [TestData.k0.pub, TestData.k1.pub]) + let mime = String(data: r.mimeEncoded, encoding: .utf8)! XCTAssertNotNil(mime.range(of: "-----BEGIN PGP MESSAGE-----")) // encrypted XCTAssertNil(mime.range(of: msg.text)) // plain text not visible XCTAssertNotNil(mime.range(of: "Subject: \(msg.subject)")) // has mime Subject header XCTAssertNil(mime.range(of: "In-Reply-To")) // Not a reply } - - func testComposeEmailInlineWithAttachment() throws { - + + func testComposeEmailInlineWithAttachment() async throws { let initialFileName = "data.txt" let urlPath = URL(fileURLWithPath: Bundle(for: type(of: self)) .path(forResource: "data", ofType: "txt")!) let fileData = try! Data(contentsOf: urlPath, options: .dataReadingMapped) - let attachment = SendableMsg.Attachment( name: initialFileName, type: "text/plain", base64: fileData.base64EncodedString() ) - let msg = SendableMsg( text: "this is the message", to: ["email@hello.com"], cc: [], bcc: [], @@ -152,45 +149,24 @@ class FlowCryptCoreTests: XCTestCase { subject: "subj", replyToMimeMsg: nil, atts: [attachment], pubKeys: nil ) - let expectation = XCTestExpectation() - - var mime: String = "" - core.composeEmail(msg: msg, fmt: .encryptInline, pubKeys: [TestData.k0.pub, TestData.k1.pub]) - .sinkFuture( - receiveValue: { composeEmailRes in - mime = String(data: composeEmailRes.mimeEncoded, encoding: .utf8)! - expectation.fulfill() - }, receiveError: {_ in } - ) - .store(in: &cancellable) - wait(for: [expectation], timeout: 3) + let r = try await core.composeEmail(msg: msg, fmt: .encryptInline, pubKeys: [TestData.k0.pub, TestData.k1.pub]) + let mime = String(data: r.mimeEncoded, encoding: .utf8)! XCTAssertNil(mime.range(of: msg.text)) // text encrypted XCTAssertNotNil(mime.range(of: "Content-Type: application/pgp-encrypted")) // encrypted XCTAssertNotNil(mime.range(of: "name=\(attachment.name)")) // attachment XCTAssertNotNil(mime.range(of: "Subject: \(msg.subject)")) // has mime Subject header } - func testEndToEnd() throws { + func testEndToEnd() async throws { let passphrase = "some pass phrase test" let email = "e2e@domain.com" let text = "this is the encrypted e2e content" let generateKeyRes = try core.generateKey(passphrase: passphrase, variant: KeyVariant.curve25519, userIds: [UserId(email: email, name: "End to end")]) let k = generateKeyRes.key let msg = SendableMsg(text: text, to: [email], cc: [], bcc: [], from: email, subject: "e2e subj", replyToMimeMsg: nil, atts: [], pubKeys: nil) - let expectation = XCTestExpectation() - - var mime: CoreRes.ComposeEmail? - core.composeEmail(msg: msg, fmt: .encryptInline, pubKeys: [k.public]) - .sinkFuture( - receiveValue: { composeEmailRes in - mime = composeEmailRes - expectation.fulfill() - }, receiveError: {_ in } - ) - .store(in: &cancellable) - wait(for: [expectation], timeout: 3) + let mime = try await core.composeEmail(msg: msg, fmt: .encryptInline, pubKeys: [k.public]) let keys = [PrvKeyInfo(private: k.private!, longid: k.ids[0].longid, passphrase: passphrase, fingerprints: k.fingerprints)] - let decrypted = try core.parseDecryptMsg(encrypted: mime?.mimeEncoded ?? Data(), keys: keys, msgPwd: nil, isEmail: true) + let decrypted = try core.parseDecryptMsg(encrypted: mime.mimeEncoded, keys: keys, msgPwd: nil, isEmail: true) XCTAssertEqual(decrypted.text, text) XCTAssertEqual(decrypted.replyType, CoreRes.ReplyType.encrypted) XCTAssertEqual(decrypted.blocks.count, 1) diff --git a/FlowCryptAppTests/Mocks/CoreComposeMessageMock.swift b/FlowCryptAppTests/Mocks/CoreComposeMessageMock.swift index ff7301df5..685414030 100644 --- a/FlowCryptAppTests/Mocks/CoreComposeMessageMock.swift +++ b/FlowCryptAppTests/Mocks/CoreComposeMessageMock.swift @@ -11,12 +11,9 @@ import Combine @testable import FlowCrypt class CoreComposeMessageMock: CoreComposeMessageType { - + var composeEmailResult: ((SendableMsg, MsgFmt, [String]?) -> (CoreRes.ComposeEmail))! - func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) -> Future { - Future { [weak self] promise in - guard let self = self else { return } - promise(.success(self.composeEmailResult(msg, fmt, pubKeys))) - } + func composeEmail(msg: SendableMsg, fmt: MsgFmt, pubKeys: [String]?) async throws -> CoreRes.ComposeEmail { + return composeEmailResult(msg, fmt, pubKeys) } } diff --git a/FlowCryptAppTests/Mocks/MessageGatewayMock.swift b/FlowCryptAppTests/Mocks/MessageGatewayMock.swift index c08c75243..101f72cc4 100644 --- a/FlowCryptAppTests/Mocks/MessageGatewayMock.swift +++ b/FlowCryptAppTests/Mocks/MessageGatewayMock.swift @@ -12,9 +12,9 @@ import Combine class MessageGatewayMock: MessageGateway { var sendMailResult: ((Data) -> (Result))! - func sendMail(input: MessageGatewayInput) -> Future { - Future { promise in - promise(self.sendMailResult(input.mime)) + func sendMail(input: MessageGatewayInput) async throws { + if case .failure(let error) = sendMailResult(input.mime) { + throw error } } } From 0c41f90e7a0de91cd59b04ab0ea2e40fd4256634 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 12 Oct 2021 19:03:47 +0300 Subject: [PATCH 6/8] Convert backupToInbox method from Promise to Future --- .../BackupSelectKeyViewController.swift | 1 - FlowCrypt/Extensions/CombineExtensions.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift index aa15bc3b0..405ee641c 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Seleckt Key Scene/BackupSelectKeyViewController.swift @@ -7,7 +7,6 @@ // import AsyncDisplayKit -import Combine import FlowCryptUI import Foundation diff --git a/FlowCrypt/Extensions/CombineExtensions.swift b/FlowCrypt/Extensions/CombineExtensions.swift index f30882d78..87e56f178 100644 --- a/FlowCrypt/Extensions/CombineExtensions.swift +++ b/FlowCrypt/Extensions/CombineExtensions.swift @@ -8,7 +8,6 @@ import Combine import Foundation -import RealmSwift extension Publisher { /// Attaches a subscriber with closure-based behaviour which will emit value and error From 06502d741664819ef1f706eadf48629f5d22b5a7 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 14 Oct 2021 22:33:16 +0300 Subject: [PATCH 7/8] Convert backupToInbox method from Promise to Future --- FlowCrypt.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 99fd53fe8..9632090d2 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -99,6 +99,7 @@ 9F2AC5B1267BDED100F6149B /* GmailSearchExpressionGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2AC5B0267BDED100F6149B /* GmailSearchExpressionGenerator.swift */; }; 9F2F206826AEEAA60044E144 /* CombineTestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2F206726AEEAA60044E144 /* CombineTestExtension.swift */; }; 9F2F207326AEECFB0044E144 /* PromiseTestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC411892681191D004C0A69 /* PromiseTestExtension.swift */; }; + 9F2F212B26B305420044E144 /* test-ci-secrets.json in Resources */ = {isa = PBXBuildFile; fileRef = 9F2F212A26B305420044E144 /* test-ci-secrets.json */; }; 9F2F217326B3269D0044E144 /* CombineExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2F217226B3269D0044E144 /* CombineExtensions.swift */; }; 9F31AB8C23298B3F00CF87EA /* Imap+retry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F31AB8B23298B3F00CF87EA /* Imap+retry.swift */; }; 9F31AB8E23298BCF00CF87EA /* Imap+folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F31AB8D23298BCF00CF87EA /* Imap+folders.swift */; }; @@ -2217,6 +2218,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9F2F212B26B305420044E144 /* test-ci-secrets.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 630e94e77d36270b34b5d9da1adbcb99fadf347e Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 14 Oct 2021 22:35:20 +0300 Subject: [PATCH 8/8] Convert backupToInbox method from Promise to Future --- FlowCrypt.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 9fa912849..63bfdab92 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -2217,7 +2217,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9F2F212B26B305420044E144 /* test-ci-secrets.json in Resources */, + 9F2F212B26B305420044E144 /* test-ci-secrets.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; };