-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
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.
Contributor
Author
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.
Contributor
Author
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.
Contributor
Author
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
Contributor
Author
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
Contributor
Author
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]? { | ||
|
|
||
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?