Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 11
Example of Combine usage#306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -6,6 +6,7 @@ import AsyncDisplayKit | ||
| import FlowCryptCommon | ||
| import FlowCryptUI | ||
| import Promises | ||
| import Combine | ||
| final class ComposeViewController: TableNodeViewController { | ||
| struct Recipient { | ||
| @@ -44,6 +45,13 @@ final class ComposeViewController: TableNodeViewController { | ||
| case subject, subjectDivider, text | ||
| } | ||
| private enum SendMessageError: Error { | ||
| case internalError(String) | ||
| case missedSender | ||
| case noPubKeyForRecipient | ||
| } | ||
| private var cancellable = Set<AnyCancellable>() | ||
| private let messageSender: MessageGateway | ||
| private let notificationCenter: NotificationCenter | ||
| private let dataService: DataServiceType & KeyDataServiceType | ||
| @@ -111,6 +119,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 +221,52 @@ extension ComposeViewController { | ||
| showSpinner("sending_title".localized) | ||
| Promise<Bool> { [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() | ||
| } | ||
| }.catch(on: .main) { [weak self] error in | ||
| self?.showAlert(error: error, message: "compose_error".localized) | ||
| } | ||
| encryptAndSendMessage() | ||
| } | ||
| private func encryptAndSendMessage() -> Promise<Bool> { | ||
| Promise<Bool> { [weak self] () -> Bool in | ||
| guard let self = self else { return false } | ||
| 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 | ||
| } | ||
| let subject = self.input.subjectReplyTitle | ||
| ?? self.contextToSend.subject | ||
| ?? "(no subject)" | ||
| guard let myPubKey = self.dataService.publicKey else { | ||
| self.showAlert(message: "compose_no_pub_sender".localized) | ||
| return false | ||
| } | ||
| guard let allRecipientPubs = self.getPubKeys(for: recipients) else { | ||
| return false | ||
| } | ||
| let encrypted = self.encryptMsg( | ||
| pubkeys: allRecipientPubs + [myPubKey], | ||
| subject: subject, | ||
| message: text, | ||
| to: recipients.map { $0.email } | ||
| ) | ||
| try await(self.messageSender.sendMail(mime: encrypted.mimeEncoded)) | ||
| return true | ||
| private func encryptAndSendMessage() { | ||
| let recipients = self.contextToSend.recipients | ||
| guard recipients.isNotEmpty else { | ||
| return | ||
| } | ||
Comment on lines
+230
to
+232
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should add back error handling, not just return and ignore. If it's input validation error from user, then we render an alert and return. If it's a nilcheck that was already supposed to be checked earlier and we're just sanity-checking our code, than that's a coding error and should result in a crash, not be ignored. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest to use implementation with | ||
| guard let text = self.contextToSend.message else { | ||
| return | ||
Comment on lines
+234
to
+235
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should add back error handling, not just return and ignore. If it's input validation error from user, then we render an alert and return. If it's a nilcheck that was already supposed to be checked earlier and we're just sanity-checking our code, than that's a coding error and should result in a crash, not be ignored. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Skip this part for this example, will return back alert and proper handling | ||
| } | ||
| let subject = self.input.subjectReplyTitle | ||
| ?? self.contextToSend.subject | ||
| ?? "(no subject)" | ||
| guard let myPubKey = self.dataService.publicKey else { | ||
| return | ||
| } | ||
Comment on lines
+242
to
244
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also here bring back alert? | ||
| guard let allRecipientPubs = getPubKeys(for: recipients) else { | ||
| return | ||
| } | ||
Comment on lines
+246
to
+248
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsure how was this handled before, looks like we should improve it, but that would be for another PR. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will use same logic as it was before. This method will return true or false | ||
| let encrypted = self.encryptMsg( | ||
| pubkeys: allRecipientPubs + [myPubKey], | ||
| subject: subject, | ||
| message: text, | ||
| to: recipients.map { $0.email } | ||
| ) | ||
| 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) | ||
Comment on lines
+259
to
+269
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR looks good, and we could actually clean up and merge it. What does the I figured What does the ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sink is used to "subscribe" to stream of events. In this example with Future(promise), there will be only 1 value, which can be accessed in There is
ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All subscriptions should be stored in cancellable to prevent their execution if view controller/service is deallocated | ||
| } | ||
| private func getPubKeys(for recepients: [Recipient]) -> [String]? { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -7,15 +7,16 @@ | ||
| // | ||
| import Foundation | ||
| import Promises | ||
| import GoogleAPIClientForREST | ||
| import GTMSessionFetcher | ||
| import Combine | ||
| extension GmailService: MessageGateway { | ||
| func sendMail(mime: Data) -> Promise<Void> { | ||
| Promise { (resolve, reject) in | ||
| func sendMail(mime: Data) -> Future<Void, Error> { | ||
tomholub marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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(())) | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this do?