From 00d7dfda166ed4946c4a94d48034a655d5c86238 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Thu, 6 May 2021 23:48:53 +0300 Subject: [PATCH 1/2] Example of Combine usage --- .../Compose/ComposeViewController.swift | 99 +++++++++++-------- .../SideMenu/Menu/MyMenuViewController.swift | 1 - .../Message Gateway/GmailService+send.swift | 13 +-- .../Message Gateway/Imap+send.swift | 17 +++- .../Message Gateway/MessageGateway.swift | 3 +- .../Backup Services/BackupService.swift | 2 +- 6 files changed, 81 insertions(+), 54 deletions(-) diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index 880886a46..732ae63de 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -6,6 +6,7 @@ import AsyncDisplayKit import FlowCryptCommon import FlowCryptUI import Promises +import Combine final class ComposeViewController: TableNodeViewController { struct Recipient { @@ -44,6 +45,14 @@ final class ComposeViewController: TableNodeViewController { case subject, subjectDivider, text } + private enum SendMessageError: Error { + case internalError(String) + case missedSender + case noPubKeyForRecipient + } + + private var cancellable = Set() + private let messageSender: MessageGateway private let notificationCenter: NotificationCenter private let dataService: DataServiceType & KeyDataServiceType @@ -111,6 +120,8 @@ final class ComposeViewController: TableNodeViewController { // temporary disable search contacts - https://github.com/FlowCrypt/flowcrypt-ios/issues/217 // showScopeAlertIfNeeded() + + cancellable.forEach { $0.cancel() } } deinit { @@ -211,57 +222,65 @@ extension ComposeViewController { showSpinner("sending_title".localized) - Promise { [weak self] in - try await(self!.encryptAndSendMessage()) - }.then(on: .main) { [weak self] sent in - if sent { // else it must have shown error to user - self?.handleSuccessfullySentMessage() + switch prepareMessage() { + case .failure(let error): + switch error { + case .internalError(let message): + showAlert(message: message) + case .missedSender: + showAlert(message: "compose_no_pub_sender".localized) + case .noPubKeyForRecipient: + // already handled + break } - }.catch(on: .main) { [weak self] error in - self?.showAlert(error: error, message: "compose_error".localized) + case .success(let data): + self.messageSender.sendMail(mime: data) + .retry(3) + .sink{ [weak self] result in + guard case .failure(let error) = result else { + return + } + self?.showAlert(error: error, message: "compose_error".localized) + } receiveValue: { [weak self] _ in + self?.handleSuccessfullySentMessage() + } + .store(in: &cancellable) } } - private func encryptAndSendMessage() -> Promise { - Promise { [weak self] () -> Bool in - guard let self = self else { return false } + private func prepareMessage() -> Result { + let recipients = contextToSend.recipients - let recipients = self.contextToSend.recipients - - guard recipients.isNotEmpty else { - assertionFailure("Recipients should not be empty. Fail in checking") - return false - } - - guard let text = self.contextToSend.message else { - assertionFailure("Text and Email should not be nil at this point. Fail in checking") - return false - } + guard recipients.isNotEmpty else { + let message = "Recipients should not be empty. Fail in checking" + return .failure(.internalError(message)) + } - let subject = self.input.subjectReplyTitle - ?? self.contextToSend.subject - ?? "(no subject)" + guard let text = contextToSend.message else { + let message = "Text and Email should not be nil at this point. Fail in checking" + return .failure(.internalError(message)) + } - guard let myPubKey = self.dataService.publicKey else { - self.showAlert(message: "compose_no_pub_sender".localized) - return false - } + let subject = self.input.subjectReplyTitle + ?? self.contextToSend.subject + ?? "(no subject)" - guard let allRecipientPubs = self.getPubKeys(for: recipients) else { - return false - } + guard let myPubKey = self.dataService.publicKey else { + return .failure(.missedSender) + } - let encrypted = self.encryptMsg( - pubkeys: allRecipientPubs + [myPubKey], - subject: subject, - message: text, - to: recipients.map { $0.email } - ) + guard let allRecipientPubs = getPubKeys(for: recipients) else { + return .failure(.noPubKeyForRecipient) + } - try await(self.messageSender.sendMail(mime: encrypted.mimeEncoded)) + let encrypted = self.encryptMsg( + pubkeys: allRecipientPubs + [myPubKey], + subject: subject, + message: text, + to: recipients.map { $0.email } + ) - return true - } + return .success(encrypted.mimeEncoded) } private func getPubKeys(for recepients: [Recipient]) -> [String]? { diff --git a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift index 1f9b6329c..3c7140690 100644 --- a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift +++ b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift @@ -4,7 +4,6 @@ import AsyncDisplayKit import FlowCryptUI -import Promises final class MyMenuViewController: ASDKViewController { private enum Constants { diff --git a/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift b/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift index 96c494705..b3794cb18 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/GmailService+send.swift @@ -7,15 +7,16 @@ // import Foundation -import Promises import GoogleAPIClientForREST import GTMSessionFetcher +import Combine extension GmailService: MessageGateway { - func sendMail(mime: Data) -> Promise { - Promise { (resolve, reject) in + func sendMail(mime: Data) -> Future { + Future { promise in guard let raw = GTLREncodeBase64(mime) else { - return reject(GmailServiceError.messageEncode) + promise(.failure(GmailServiceError.messageEncode)) + return } let gtlMessage = GTLRGmail_Message() @@ -29,10 +30,10 @@ extension GmailService: MessageGateway { self.gmailService.executeQuery(querySend) { (_, _, error) in if let error = error { - reject(GmailServiceError.providerError(error)) + promise(.failure(GmailServiceError.providerError(error))) return } - resolve(()) + 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 1323301bc..3a2b3d6af 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift @@ -3,15 +3,22 @@ // import Foundation -import Promises +import Combine extension Imap: MessageGateway { - func sendMail(mime: Data) -> Promise { - Promise { [weak self] resolve, reject in - guard let self = self else { return reject(AppErr.nilSelf) } + func sendMail(mime: Data) -> Future { + Future { [weak self] promise in + guard let self = self else { return promise(.failure(AppErr.nilSelf)) } + self.smtpSess? .sendOperation(with: mime) - .start(self.finalizeVoid("send", resolve, reject, retry: { self.sendMail(mime: mime) })) + .start { error in + if let error = error { + promise(.failure(error)) + } else { + promise(.success(())) + } + } } } } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Gateway/MessageGateway.swift b/FlowCrypt/Functionality/Mail Provider/Message Gateway/MessageGateway.swift index abadd0182..42108c01c 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/MessageGateway.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/MessageGateway.swift @@ -8,7 +8,8 @@ import Foundation import Promises +import Combine protocol MessageGateway { - func sendMail(mime: Data) -> Promise + func sendMail(mime: Data) -> Future } diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index 889dd39f2..7bdc0bdf5 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -76,7 +76,7 @@ extension BackupService: BackupServiceType { atts: attachments ) let backupEmail = try self.core.composeEmail(msg: message, fmt: .plain, pubKeys: nil) - try await(messageSender.sendMail(mime: backupEmail.mimeEncoded)) +// try await(messageSender.sendMail(mime: backupEmail.mimeEncoded)) } } From 41e6a0cfff0e256304fd835adb01306d70abe041 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Tue, 13 Jul 2021 23:03:02 +0300 Subject: [PATCH 2/2] Example of Combine --- .../Compose/ComposeViewController.swift | 70 ++++++++----------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index 732ae63de..71e8b1d27 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -52,7 +52,6 @@ final class ComposeViewController: TableNodeViewController { } private var cancellable = Set() - private let messageSender: MessageGateway private let notificationCenter: NotificationCenter private let dataService: DataServiceType & KeyDataServiceType @@ -222,65 +221,52 @@ extension ComposeViewController { showSpinner("sending_title".localized) - switch prepareMessage() { - case .failure(let error): - switch error { - case .internalError(let message): - showAlert(message: message) - case .missedSender: - showAlert(message: "compose_no_pub_sender".localized) - case .noPubKeyForRecipient: - // already handled - break - } - case .success(let data): - self.messageSender.sendMail(mime: data) - .retry(3) - .sink{ [weak self] result in - guard case .failure(let error) = result else { - return - } - self?.showAlert(error: error, message: "compose_error".localized) - } receiveValue: { [weak self] _ in - self?.handleSuccessfullySentMessage() - } - .store(in: &cancellable) - } + encryptAndSendMessage() } - private func prepareMessage() -> Result { - let recipients = contextToSend.recipients - + private func encryptAndSendMessage() { + let recipients = self.contextToSend.recipients + guard recipients.isNotEmpty else { - let message = "Recipients should not be empty. Fail in checking" - return .failure(.internalError(message)) + return } - - guard let text = contextToSend.message else { - let message = "Text and Email should not be nil at this point. Fail in checking" - return .failure(.internalError(message)) + + guard let text = self.contextToSend.message else { + return } - + let subject = self.input.subjectReplyTitle ?? self.contextToSend.subject ?? "(no subject)" - + guard let myPubKey = self.dataService.publicKey else { - return .failure(.missedSender) + return } - + guard let allRecipientPubs = getPubKeys(for: recipients) else { - return .failure(.noPubKeyForRecipient) + return } - + let encrypted = self.encryptMsg( pubkeys: allRecipientPubs + [myPubKey], subject: subject, message: text, to: recipients.map { $0.email } ) - - return .success(encrypted.mimeEncoded) + + self.messageSender + .sendMail(mime: encrypted.mimeEncoded) + .sink( + receiveCompletion: { [weak self] result in + guard case .failure(let error) = result else { + return + } + self?.showAlert(error: error, message: "compose_error".localized) + }, + receiveValue: { [weak self] in + self?.handleSuccessfullySentMessage() + }) + .store(in: &cancellable) } private func getPubKeys(for recepients: [Recipient]) -> [String]? {