From 4a97e715b25b79df92e51255ad80eaab46ff4d3c Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Tue, 9 May 2023 14:31:40 +0200 Subject: [PATCH 1/4] refactor: More consistant error handling --- Mail/Views/Onboarding/OnboardingView.swift | 6 +- Mail/Views/SplitView.swift | 2 +- MailCore/API/ApiError.swift | 187 --------------------- MailCore/API/MailApiError.swift | 94 +++++++++++ MailCore/API/MailApiFetcher.swift | 4 +- MailCore/API/MailError.swift | 89 +++++----- 6 files changed, 142 insertions(+), 240 deletions(-) delete mode 100644 MailCore/API/ApiError.swift create mode 100644 MailCore/API/MailApiError.swift diff --git a/Mail/Views/Onboarding/OnboardingView.swift b/Mail/Views/Onboarding/OnboardingView.swift index 3918ccbe8..405f75975 100644 --- a/Mail/Views/Onboarding/OnboardingView.swift +++ b/Mail/Views/Onboarding/OnboardingView.swift @@ -18,8 +18,8 @@ import AuthenticationServices import InfomaniakCoreUI -import InfomaniakDI import InfomaniakCreateAccount +import InfomaniakDI import InfomaniakLogin import Lottie import MailCore @@ -71,7 +71,7 @@ struct Slide: Identifiable { class LoginHandler: InfomaniakLoginDelegate, ObservableObject { @LazyInjectService var loginService: InfomaniakLoginable @LazyInjectService var matomo: MatomoUtils - + @Published var isLoading = false @Published var isPresentingErrorAlert = false var sceneDelegate: SceneDelegate? @@ -129,7 +129,7 @@ class LoginHandler: InfomaniakLoginDelegate, ObservableObject { _ = try await AccountManager.instance.createAndSetCurrentAccount(code: code, codeVerifier: verifier) sceneDelegate?.showMainView() UIApplication.shared.registerForRemoteNotifications() - } catch MailError.noMailbox { + } catch let error as MailError where error == MailError.noMailbox { sceneDelegate?.showNoMailboxView() } catch { if let previousAccount = previousAccount { diff --git a/Mail/Views/SplitView.swift b/Mail/Views/SplitView.swift index b1c2beed8..b29ecf6b3 100644 --- a/Mail/Views/SplitView.swift +++ b/Mail/Views/SplitView.swift @@ -108,7 +108,7 @@ struct SplitView: View { if let tappedNotificationThread = tappedNotificationMessage?.originalThread { navigationStore.threadPath = [tappedNotificationThread] } else { - IKSnackBar.showSnackBar(message: MailError.messageNotFound.errorDescription ?? "") + IKSnackBar.showSnackBar(message: MailError.messageNotFound.errorDescription) } } .onAppear { diff --git a/MailCore/API/ApiError.swift b/MailCore/API/ApiError.swift deleted file mode 100644 index 21ebe495a..000000000 --- a/MailCore/API/ApiError.swift +++ /dev/null @@ -1,187 +0,0 @@ -/* - Infomaniak Mail - iOS App - Copyright (C) 2022 Infomaniak Network SA - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -import Foundation - -enum ApiErrorCode: String { - // General - case notAuthorized = "not_authorized" - - // Folder - case folderUnableToCreate = "folder__unable_to_create" - case folderUnableToUpdate = "folder__unable_to_update" - case folderUnableToDelete = "folder__unable_to_delete" - case folderUnableToFlush = "folder__unable_to_flush" - case protectedFolder = "folder__protected_folder" - case folderUnableToMoveInSub = "folder__unable_to_move_folder_in_its_sub_folders" - case destinationFolderAlreadyExists = "folder__destination_folder_already_exists" - case rootDestinationNotExists = "folder__root_destination_not_exists" - case folderAlreadyExists = "folder__destination_already_exists" - case folderNotExists = "folder__not_exists" - - // Mail - case moveDestinationNotFound = "mail__move_destination_folder_not_found" - case cannotConnectToIMAPServer = "mail__cannot_connect_to_server" - case IMAPAuthFailed = "mail__imap_authentication_failed" - case IMAPUnableToParseResponse = "mail__imap_unable_to_parse_response" - case IMAPConnectionTimedOut = "mail__imap_connection_timedout" - case cannotConnectToSMTPServer = "mail__cannot_connect_to_smtp_server" - case SMTPAuthFailed = "mail__smtp_authentication_failed" - case messageNotFound = "mail__message_not_found" - case messageAttachmentNotFound = "mail__message_attachment_not_found" - case unableToUndoMoveAction = "mail__unable_to_undo_move_action" - case unableToMoveEmails = "mail__unable_to_move_emails" - - // Draft - case draftAttachmentNotFound = "draft__attachment_not_found" - case draftNotFound = "draft__not_found" - case draftMessageNotFound = "draft__message_not_found" - case draftTooManyRecipients = "draft__to_many_recipients" - case draftMaxAttachmentsSizeReached = "draft__max_attachments_size_reached" - case draftNeedAtLeastOneRecipient = "draft__need_at_least_one_recipient_to_be_sent" - case draftAlreadyScheduledOrSent = "draft__cannot_modify_scheduled_or_already_sent_message" - case draftCannotCancelNonScheduledMessage = "draft__cannot_cancel_non_scheduled_message" - case draftCannotForwardMoreThanOneMessageInline = "draft__cannot_forward_more_than_one_message_inline" - case draftCannotMoveScheduledMessage = "draft__cannot_move_scheduled_message" - - // Send - case sendFromRefused = "send__server_refused_from" - case sendRecipientRefused = "send__server_refused_all_recipients" - case sendLimitExceeded = "send__server_rate_limit_exceeded" - case sendUnknownError = "send__server_unknown_error" - case sendDailyLimitReached = "send__server_daily_limit_reached" - case sendSpamRejected = "send__spam_rejected" - case sendSenderMismatch = "send__sender_mismatch" - - // Attachment - case attachmentNotValid = "attachment__not_valid" - case attachmentNotFound = "attachment__not_found" - case attachmentCannotRender = "attachment__cannot_render" - case attachmentRenderError = "attachment__error_while_render" - case attachmentMissingFilenameOrMimeType = "attachment__missing_filename_or_mimetype" - case attachmentUploadIncorrect = "attachment__incorrect_disposition" - case attachmentUploadContentIdNotValid = "attachment__content_id_not_valid" - case attachmentAddFromDriveFailed = "attachment__add_attachment_from_drive_fail" - case attachmentStoreToDriveFailed = "attachment__store_to_drive_fail" - - // Message - case messageUidIsNotValid = "message__uid_is_not_valid" - - var localizedDescription: String { - switch self { - case .notAuthorized: - return "Not authorized" - case .folderUnableToCreate: - return "Unable to create folder" - case .folderUnableToUpdate: - return "Unable to update folder" - case .folderUnableToDelete: - return "Unable to delete folder" - case .folderUnableToFlush: - return "Unable to flush folder" - case .protectedFolder: - return "Protected folder" - case .folderUnableToMoveInSub: - return "Unable to move folder in its sub folders" - case .destinationFolderAlreadyExists: - return "Destination folder already exists" - case .rootDestinationNotExists: - return "Root destination does not exist" - case .folderAlreadyExists: - return "Destination already exists" - case .folderNotExists: - return "Folder does not exist" - case .moveDestinationNotFound: - return "Move destination folder not found" - case .cannotConnectToIMAPServer: - return "Cannot connect to IMAP server" - case .IMAPAuthFailed: - return "IMAP authentication failed" - case .IMAPUnableToParseResponse: - return "Unable to parse IMAP response" - case .IMAPConnectionTimedOut: - return "IMAP connection timed out" - case .cannotConnectToSMTPServer: - return "Cannot connect to SMTP server" - case .SMTPAuthFailed: - return "SMTP authentication failed" - case .messageNotFound: - return "Message not found" - case .messageAttachmentNotFound: - return "Message attachment not found" - case .unableToUndoMoveAction: - return "Unable to undo move action" - case .unableToMoveEmails: - return "Unable to move emails" - case .draftAttachmentNotFound: - return "Attachment not found" - case .draftNotFound: - return "Draft not found" - case .draftMessageNotFound: - return "Message not found" - case .draftTooManyRecipients: - return "Too many recipients" - case .draftMaxAttachmentsSizeReached: - return "Max attachments size reached" - case .draftNeedAtLeastOneRecipient: - return "Draft needs at least one recipient to be sent" - case .draftAlreadyScheduledOrSent: - return "Cannot modify scheduled or already sent message" - case .draftCannotCancelNonScheduledMessage: - return "Cannot cancel non scheduled message" - case .draftCannotForwardMoreThanOneMessageInline: - return "Cannot forward more than one message inline" - case .draftCannotMoveScheduledMessage: - return "Cannot move scheduled message" - case .sendFromRefused: - return "Server refused from" - case .sendRecipientRefused: - return "Server refused all recipients" - case .sendLimitExceeded: - return "Rate limit exceeded" - case .sendUnknownError: - return "Unknown server error" - case .sendDailyLimitReached: - return "Daily limit reached" - case .sendSpamRejected: - return "Spam rejected" - case .sendSenderMismatch: - return "Sender mismatch" - case .attachmentNotValid: - return "Attachment not valid" - case .attachmentNotFound: - return "Attachment not found" - case .attachmentCannotRender: - return "Attachment cannot render" - case .attachmentRenderError: - return "Attachment render error" - case .attachmentMissingFilenameOrMimeType: - return "Attachment missing filename or mime type" - case .attachmentUploadIncorrect: - return "Attachment incorrect disposition" - case .attachmentUploadContentIdNotValid: - return "Attachment content ID not valid" - case .attachmentAddFromDriveFailed: - return "Add attachment from drive failed" - case .attachmentStoreToDriveFailed: - return "Store attachment to drive failed" - case .messageUidIsNotValid: - return "Message UID is not valid" - } - } -} diff --git a/MailCore/API/MailApiError.swift b/MailCore/API/MailApiError.swift new file mode 100644 index 000000000..ff191881a --- /dev/null +++ b/MailCore/API/MailApiError.swift @@ -0,0 +1,94 @@ +/* + Infomaniak Mail - iOS App + Copyright (C) 2022 Infomaniak Network SA + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +import Foundation + +class MailApiError: MailError { + static let allErrors: [MailApiError] = [ + // General + MailApiError(code: "not_authorized"), + + // Folder + MailApiError(code: "folder__unable_to_create"), + MailApiError(code: "folder__unable_to_update"), + MailApiError(code: "folder__unable_to_delete"), + MailApiError(code: "folder__unable_to_flush"), + MailApiError(code: "folder__protected_folder"), + MailApiError(code: "folder__unable_to_move_folder_in_its_sub_folders"), + MailApiError(code: "folder__destination_folder_already_exists"), + MailApiError(code: "folder__root_destination_not_exists"), + MailApiError(code: "folder__destination_already_exists"), + MailApiError(code: "folder__not_exists"), + + // Mail + MailApiError(code: "mail__move_destination_folder_not_found"), + MailApiError(code: "mail__cannot_connect_to_server"), + MailApiError(code: "mail__imap_authentication_failed"), + MailApiError(code: "mail__imap_unable_to_parse_response"), + MailApiError(code: "mail__imap_connection_timedout"), + MailApiError(code: "mail__cannot_connect_to_smtp_server"), + MailApiError(code: "mail__smtp_authentication_failed"), + MailApiError(code: "mail__message_not_found"), + MailApiError(code: "mail__message_attachment_not_found"), + MailApiError(code: "mail__unable_to_undo_move_action"), + MailApiError(code: "mail__unable_to_move_emails"), + + // Draft + MailApiError(code: "draft__attachment_not_found"), + MailApiError(code: "draft__not_found"), + MailApiError(code: "draft__message_not_found"), + MailApiError(code: "draft__to_many_recipients"), + MailApiError(code: "draft__max_attachments_size_reached"), + MailApiError(code: "draft__need_at_least_one_recipient_to_be_sent"), + MailApiError(code: "draft__cannot_modify_scheduled_or_already_sent_message"), + MailApiError(code: "draft__cannot_cancel_non_scheduled_message"), + MailApiError(code: "draft__cannot_forward_more_than_one_message_inline"), + MailApiError(code: "draft__cannot_move_scheduled_message"), + + // Send + MailApiError(code: "send__server_refused_from"), + MailApiError(code: "send__server_refused_all_recipients"), + MailApiError(code: "send__server_rate_limit_exceeded"), + MailApiError(code: "send__server_unknown_error"), + MailApiError(code: "send__server_daily_limit_reached"), + MailApiError(code: "send__spam_rejected"), + MailApiError(code: "send__sender_mismatch"), + + // Attachment + MailApiError(code: "attachment__not_valid"), + MailApiError(code: "attachment__not_found"), + MailApiError(code: "attachment__cannot_render"), + MailApiError(code: "attachment__error_while_render"), + MailApiError(code: "attachment__missing_filename_or_mimetype"), + MailApiError(code: "attachment__incorrect_disposition"), + MailApiError(code: "attachment__content_id_not_valid"), + MailApiError(code: "attachment__add_attachment_from_drive_fail"), + MailApiError(code: "attachment__store_to_drive_fail"), + + // Message + MailApiError(code: "message__uid_is_not_valid") + ] + + static func mailApiErrorFromCode(_ code: String) -> MailApiError? { + return allErrors.first { $0.code == code } + } + + static func mailApiErrorWithFallback(apiErrorCode: String) -> MailError { + return allErrors.first { $0.code == apiErrorCode } ?? MailError.unknownError + } +} diff --git a/MailCore/API/MailApiFetcher.swift b/MailCore/API/MailApiFetcher.swift index 5f34c6cbc..82943822c 100644 --- a/MailCore/API/MailApiFetcher.swift +++ b/MailCore/API/MailApiFetcher.swift @@ -43,9 +43,9 @@ public class MailApiFetcher: ApiFetcher { do { return try await super.perform(request: request) } catch InfomaniakError.apiError(let apiError) { - throw MailError.apiError(apiError) + throw MailApiError.mailApiErrorWithFallback(apiErrorCode: apiError.code) } catch InfomaniakError.serverError(statusCode: let statusCode) { - throw MailError.serverError(statusCode: statusCode) + throw MailServerError(httpStatus: statusCode) } catch { if let afError = error.asAFError { if case .responseSerializationFailed(let reason) = afError, diff --git a/MailCore/API/MailError.swift b/MailCore/API/MailError.swift index 4e5941a2f..c1dfb6b10 100644 --- a/MailCore/API/MailError.swift +++ b/MailCore/API/MailError.swift @@ -23,64 +23,59 @@ import MailResources extension ApiError: CustomStringConvertible {} -struct AFErrorWithContext: LocalizedError { +class AFErrorWithContext: MailError { let request: DataRequest let afError: AFError - public var errorDescription: String? { - return MailError.unknownError.errorDescription + init(request: DataRequest, afError: AFError) { + self.request = request + self.afError = afError + super.init(code: "afErrorWithContext", shouldDisplay: false) } } -public enum MailError: LocalizedError { - case apiError(ApiError) - case serverError(statusCode: Int) - case noToken - case resourceError - case unknownError - case unknownToken - case noMailbox - case messageNotFound - case folderNotFound - case addressBookNotFound - case contactNotFound - case attachmentsSizeLimitReached +public class MailError: LocalizedError { + public let code: String + public let errorDescription: String + public let shouldDisplay: Bool - public var errorDescription: String? { - switch self { - case .apiError(let apiError): - if let code = ApiErrorCode(rawValue: apiError.code) { - return code.localizedDescription - } - return apiError.description - case .noToken: - return "No API token" - case .resourceError: - return "Resource error" - case .unknownError: - return "Unknown error" - case .serverError: - return "Server error" - case .unknownToken: - return "Unknown token" - case .noMailbox: - return "No Mailbox" - case .folderNotFound: - return "Folder not found" - case .addressBookNotFound: - return "Address Book not found" - case .contactNotFound: - return "Contact not found" - case .messageNotFound: - return "Message not found" - case .attachmentsSizeLimitReached: - return MailResourcesStrings.Localizable.attachmentFileLimitReached - } + init(code: String, localizedDescription: String? = nil, shouldDisplay: Bool = false) { + self.code = code + errorDescription = localizedDescription ?? "Unknown error" + self.shouldDisplay = shouldDisplay } + + public static let unknownError = MailError(code: "unknownError", shouldDisplay: true) + public static let noToken = MailError(code: "noToken", shouldDisplay: true) + public static let resourceError = MailError(code: "resourceError", shouldDisplay: true) + public static let unknownToken = MailError(code: "unknownToken", shouldDisplay: true) + public static let noMailbox = MailError(code: "noMailbox", shouldDisplay: true) + public static let folderNotFound = MailError(code: "folderNotFound", shouldDisplay: true) + public static let addressBookNotFound = MailError(code: "addressBookNotFound", shouldDisplay: true) + public static let contactNotFound = MailError(code: "contactNotFound", shouldDisplay: true) + public static let messageNotFound = MailError(code: "messageNotFound", shouldDisplay: true) + public static let attachmentsSizeLimitReached = MailError(code: "attachmentsSizeLimitReached", + localizedDescription: MailResourcesStrings.Localizable + .attachmentFileLimitReached, + shouldDisplay: true) } extension MailError: Identifiable { public var id: String { - return errorDescription ?? UUID().uuidString + return code + } +} + +extension MailError: Equatable { + public static func == (lhs: MailError, rhs: MailError) -> Bool { + return lhs.code == rhs.code + } +} + +public class MailServerError: MailError { + let httpStatus: Int + init(httpStatus: Int) { + self.httpStatus = httpStatus + super.init(code: "serverError") } } From 294144b742c57153f02957c67cabcf4f78b7ac0c Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Tue, 9 May 2023 15:14:21 +0200 Subject: [PATCH 2/4] feat: Translate some API error codes --- .../Bottom sheets/RestoreEmailsView.swift | 2 +- MailCore/API/MailApiError.swift | 35 ++++++++++++++---- MailCore/Utils/Error+Extension.swift | 30 +++++++++------ .../Localizable/de.lproj/Localizable.strings | Bin 65332 -> 65502 bytes .../Localizable/en.lproj/Localizable.strings | Bin 61912 -> 62074 bytes .../Localizable/es.lproj/Localizable.strings | Bin 65290 -> 65458 bytes .../Localizable/fr.lproj/Localizable.strings | Bin 65716 -> 65884 bytes .../Localizable/it.lproj/Localizable.strings | Bin 65196 -> 65382 bytes 8 files changed, 47 insertions(+), 20 deletions(-) diff --git a/Mail/Views/Bottom sheets/RestoreEmailsView.swift b/Mail/Views/Bottom sheets/RestoreEmailsView.swift index 0a7ff7de9..71764957c 100644 --- a/Mail/Views/Bottom sheets/RestoreEmailsView.swift +++ b/Mail/Views/Bottom sheets/RestoreEmailsView.swift @@ -76,7 +76,7 @@ struct RestoreEmailsView: View { Task { await tryOrDisplayError { try await mailboxManager.apiFetcher.restoreBackup(mailbox: mailboxManager.mailbox, date: selectedDate) - await IKSnackBar.showSnackBar(message: MailResourcesStrings.Localizable.snackbarSuccessfulRestoration) + IKSnackBar.showSnackBar(message: MailResourcesStrings.Localizable.snackbarRestorationLaunched) } } } diff --git a/MailCore/API/MailApiError.swift b/MailCore/API/MailApiError.swift index ff191881a..ffb145548 100644 --- a/MailCore/API/MailApiError.swift +++ b/MailCore/API/MailApiError.swift @@ -17,6 +17,7 @@ */ import Foundation +import MailResources class MailApiError: MailError { static let allErrors: [MailApiError] = [ @@ -32,7 +33,11 @@ class MailApiError: MailError { MailApiError(code: "folder__unable_to_move_folder_in_its_sub_folders"), MailApiError(code: "folder__destination_folder_already_exists"), MailApiError(code: "folder__root_destination_not_exists"), - MailApiError(code: "folder__destination_already_exists"), + MailApiError( + code: "folder__destination_already_exists", + localizedDescription: MailResourcesStrings.Localizable.errorNewFolderAlreadyExists, + shouldDisplay: true + ), MailApiError(code: "folder__not_exists"), // Mail @@ -52,18 +57,34 @@ class MailApiError: MailError { MailApiError(code: "draft__attachment_not_found"), MailApiError(code: "draft__not_found"), MailApiError(code: "draft__message_not_found"), - MailApiError(code: "draft__to_many_recipients"), + MailApiError( + code: "draft__to_many_recipients", + localizedDescription: MailResourcesStrings.Localizable.tooManyRecipients, + shouldDisplay: true + ), MailApiError(code: "draft__max_attachments_size_reached"), - MailApiError(code: "draft__need_at_least_one_recipient_to_be_sent"), - MailApiError(code: "draft__cannot_modify_scheduled_or_already_sent_message"), + MailApiError( + code: "draft__need_at_least_one_recipient_to_be_sent", + localizedDescription: MailResourcesStrings.Localizable.errorAtLeastOneRecipient, + shouldDisplay: true + ), + MailApiError( + code: "draft__cannot_modify_scheduled_or_already_sent_message", + localizedDescription: MailResourcesStrings.Localizable.errorEditScheduledMessage, + shouldDisplay: true + ), MailApiError(code: "draft__cannot_cancel_non_scheduled_message"), MailApiError(code: "draft__cannot_forward_more_than_one_message_inline"), MailApiError(code: "draft__cannot_move_scheduled_message"), // Send MailApiError(code: "send__server_refused_from"), - MailApiError(code: "send__server_refused_all_recipients"), - MailApiError(code: "send__server_rate_limit_exceeded"), + MailApiError(code: "send__server_refused_all_recipients", + localizedDescription: MailResourcesStrings.Localizable.errorRefusedRecipients, + shouldDisplay: true), + MailApiError(code: "send__server_rate_limit_exceeded", + localizedDescription: MailResourcesStrings.Localizable.errorSendLimitExceeded, + shouldDisplay: true), MailApiError(code: "send__server_unknown_error"), MailApiError(code: "send__server_daily_limit_reached"), MailApiError(code: "send__spam_rejected"), @@ -89,6 +110,6 @@ class MailApiError: MailError { } static func mailApiErrorWithFallback(apiErrorCode: String) -> MailError { - return allErrors.first { $0.code == apiErrorCode } ?? MailError.unknownError + return mailApiErrorFromCode(apiErrorCode) ?? MailError.unknownError } } diff --git a/MailCore/Utils/Error+Extension.swift b/MailCore/Utils/Error+Extension.swift index 22a0e7d5b..f5cd5caca 100644 --- a/MailCore/Utils/Error+Extension.swift +++ b/MailCore/Utils/Error+Extension.swift @@ -16,6 +16,7 @@ along with this program. If not, see . */ +import CocoaLumberjackSwift import Foundation import InfomaniakCoreUI @@ -23,12 +24,7 @@ public func tryOrDisplayError(_ body: () throws -> Void) { do { try body() } catch { - if error.shouldDisplay { - Task.detached { - await IKSnackBar.showSnackBar(message: error.localizedDescription) - } - } - print("Error: \(error)") + displayErrorIfNeeded(error: error) } } @@ -36,12 +32,22 @@ public func tryOrDisplayError(_ body: () async throws -> Void) async { do { try await body() } catch { - if error.shouldDisplay { - Task.detached { - await IKSnackBar.showSnackBar(message: error.localizedDescription) - } + displayErrorIfNeeded(error: error) + } +} + +private func displayErrorIfNeeded(error: Error) { + if let error = error as? MailError, + error.shouldDisplay { + Task.detached { + await IKSnackBar.showSnackBar(message: error.errorDescription) + } + DDLogError("MailError: \(error)") + } else if error.shouldDisplay { + Task.detached { + await IKSnackBar.showSnackBar(message: error.localizedDescription) } - print("Error: \(error)") + DDLogError("Error: \(error)") } } @@ -50,7 +56,7 @@ public extension Error { switch asAFError { case .explicitlyCancelled: return false - case let .sessionTaskFailed(error): + case .sessionTaskFailed(let error): return (error as NSError).code != NSURLErrorNotConnectedToInternet default: return true diff --git a/MailResources/Localizable/de.lproj/Localizable.strings b/MailResources/Localizable/de.lproj/Localizable.strings index 9071dd613a75adaf28fa3a975c6033657c655b76..e913d235a0aff18e7cdbb3d8c9566eed967fa563 100644 GIT binary patch delta 293 zcmYL^&q@Me6vdCYQTH-nn=uFqTBx5AQ?FnkA+*++@eidVI)c4L`t5pwmI~uTxYY*; zdWN8-^&4Bn$G!J_+;h)8cYDl!{A4%fOu40jhI%F@yil$l$wLJisiU#R%j22XQLI~9 zZOv(3gDUdK_fTG_HU2djP*ru9^;53OByXAU_2Pk{84{7E48*8MC^x}#s*i(EL2|ir z0{fmX+4Wa0@xS*uyZCgj8b}97_gHA)H9$UrIMt(Tbfc~+cC&MesiE|mE*lnXh~PZg e$)RXw%>Ehv@4IkQU#|Kmhw1%OUaWK7sqhQI9zaU~ delta 165 zcmccjpLxqa<_(IBs*Vh~3`q<{4EYS148;to3coH2lVce< z41IyZc?>#0MFtFpK+=~Xk)aYu8v*%748~w_O9m?-%LqssGFWb&#+W)mF&Jo8G7zQ$ tZ760)1M+ehg222Ipn*jUiIW%X5#6jii7RFD;gvFz-+$uR9Qo}c2LLr*DNXiUAB644Di$KwJQXsSFAX;XpEzA%!8AA(0`EA#W0+v}Gbg34;|- zDukgFD5(Qv88BD^NnfCnN+4|n&-i delta 169 zcmezMg!#r}<_*e>s*Vh~3`q<{4EYS148;to3Lf<#$?=RF zhQ2`IJO&-0A_E3PAnD7H$WRHSjevY324k?eC4&`^WdtM*87w!?V0_r284NTl83 uHWV|Y0eLwLL1119(7+;~AtgZDfktnh*EKtC^2Eb3ll@s|e%mDylU?^Px diff --git a/MailResources/Localizable/es.lproj/Localizable.strings b/MailResources/Localizable/es.lproj/Localizable.strings index 20be060ac7ac7b8b07c97ef30ff824c42182d3d2..1cd94d102ba825b605b99ab8bfcb15fe26169e70 100644 GIT binary patch delta 263 zcmeDB$Gqu3^9E%`#Q=s3hD?SWAT9vHR0aixa3GnYkdq%TlOC6G1(@{Jgbfhef-&HpAIO`e?UAhr4aS8dM89uLGdO@YRy0F6xpT4oGX zV+v#?12NEGAZ-bhHD)kpNMbOUeD1cmHpnp`pCkcIuK=2!4^)#4GMPaM=rCI#R$_1k PyCZe-o@U9(mbb(K4pKRG delta 151 zcmdn=pSkNF^9E%`RY!(gh9rg}hJ1!hhGK?P1_cHeAejv0mok(9VJc8AbrPfW45*bgOU diff --git a/MailResources/Localizable/fr.lproj/Localizable.strings b/MailResources/Localizable/fr.lproj/Localizable.strings index aae046dda9ed8523fc0b13677e6f11d8198316cf..ba87e45461954133c3e95756681df796e7ae5a84 100644 GIT binary patch delta 307 zcmYL^y=nqc5QUFmAr`iRX;#4|2^d`$*`)CS5(p9yOLbQwQ8yZeSn3l*w(}{3wN0ya z`hax`TTK=6T^qT~-<>mO&a7wg?|FQcjb$J8tgd?4E69E0J|VVI;LD4C z7hisOTxY`1Ln7KAw~27GOE(;-J*Yb@w81%KZ47dvOV?IKt&;#tmntZ!9JmqB>@yP0 meJ!eYb_Ptnn0;Y2mA*1Jp1T(RMyCb32XctMAO3Q>Q}Q1HtwMGH delta 165 zcmcc9#ImK4WrHH4sv|=#LlQ#~Lq0<$Loq`tg93vKkW2>hOBqUlFcm15I`N=rATX~4XkZc0kP@KnK%+N*n0PUDa{5G>&9}dw=Kuh3Dkl8^ diff --git a/MailResources/Localizable/it.lproj/Localizable.strings b/MailResources/Localizable/it.lproj/Localizable.strings index 71a9f077df78b48e7c22b38f15241e3e5f2ca035..246d5e335f5a4bcd907fb8eb390f9b85152f18ce 100644 GIT binary patch delta 257 zcmZ4Um-*R0<_*e>iUAB644Di$KwJQXsSFAX;XpEzA%!8AA(0`EA#W0+v}Gbg34;|- zDukgFD5(Qv88BD^NnfCnN+4|n;q(%0_kL+3sM^XnO=|N0&m5aSe%;M6S*t=++l0XsXm|?H z@HC*6#z2*(Kvpsk1C0mLmOxo!26Kia27}24Kg1s*Vh~3`q<{4EYS148;to3Lf<#$?=RF zhQ2`IJO&-0A_E3PAnD7H$WRHSjevY324k?eC4&`^WdtM*87w!?U_3oRJ{V|LG7zQ$ nZ760)1M+ehg222Ipn*k`4Q`2Uo;=AYd9t#B%;x)F4|4zj^6Vvc From d44f3e1909b120c834184b15e93470e4993e1d0c Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Wed, 10 May 2023 11:53:01 +0200 Subject: [PATCH 3/4] feat: Sentry log for unknown MailError --- MailCore/Utils/Error+Extension.swift | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/MailCore/Utils/Error+Extension.swift b/MailCore/Utils/Error+Extension.swift index f5cd5caca..d69dc43df 100644 --- a/MailCore/Utils/Error+Extension.swift +++ b/MailCore/Utils/Error+Extension.swift @@ -19,6 +19,7 @@ import CocoaLumberjackSwift import Foundation import InfomaniakCoreUI +import Sentry public func tryOrDisplayError(_ body: () throws -> Void) { do { @@ -37,10 +38,18 @@ public func tryOrDisplayError(_ body: () async throws -> Void) async { } private func displayErrorIfNeeded(error: Error) { - if let error = error as? MailError, - error.shouldDisplay { - Task.detached { - await IKSnackBar.showSnackBar(message: error.errorDescription) + if let error = error as? MailError { + if error.shouldDisplay { + Task.detached { + await IKSnackBar.showSnackBar(message: error.errorDescription) + } + } else { + SentrySDK.capture(message: "Encountered error that we didn't display to the user") { scope in + scope.setContext( + value: ["Code": error.code, "Raw": error], + key: "Error" + ) + } } DDLogError("MailError: \(error)") } else if error.shouldDisplay { From 49265e9423e32ce0ebfb0b0ea7ccaf641ef700fd Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Thu, 11 May 2023 10:09:55 +0200 Subject: [PATCH 4/4] feat: Translate more errors --- MailCore/API/MailApiError.swift | 4 +++- MailCore/API/MailError.swift | 16 +++++++++++----- .../Localizable/de.lproj/Localizable.strings | Bin 65502 -> 66146 bytes .../Localizable/en.lproj/Localizable.strings | Bin 62074 -> 62682 bytes .../Localizable/es.lproj/Localizable.strings | Bin 65458 -> 66094 bytes .../Localizable/fr.lproj/Localizable.strings | Bin 65884 -> 66512 bytes .../Localizable/it.lproj/Localizable.strings | Bin 65382 -> 66016 bytes 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/MailCore/API/MailApiError.swift b/MailCore/API/MailApiError.swift index ffb145548..4d3c39b02 100644 --- a/MailCore/API/MailApiError.swift +++ b/MailCore/API/MailApiError.swift @@ -38,7 +38,9 @@ class MailApiError: MailError { localizedDescription: MailResourcesStrings.Localizable.errorNewFolderAlreadyExists, shouldDisplay: true ), - MailApiError(code: "folder__not_exists"), + MailApiError(code: "folder__not_exists", + localizedDescription: MailResourcesStrings.Localizable.errorFolderNotFound, + shouldDisplay: true), // Mail MailApiError(code: "mail__move_destination_folder_not_found"), diff --git a/MailCore/API/MailError.swift b/MailCore/API/MailError.swift index c1dfb6b10..3bbb61364 100644 --- a/MailCore/API/MailError.swift +++ b/MailCore/API/MailError.swift @@ -39,9 +39,11 @@ public class MailError: LocalizedError { public let errorDescription: String public let shouldDisplay: Bool - init(code: String, localizedDescription: String? = nil, shouldDisplay: Bool = false) { + init(code: String, + localizedDescription: String = MailResourcesStrings.Localizable.errorUnknown, + shouldDisplay: Bool = false) { self.code = code - errorDescription = localizedDescription ?? "Unknown error" + errorDescription = localizedDescription self.shouldDisplay = shouldDisplay } @@ -49,11 +51,15 @@ public class MailError: LocalizedError { public static let noToken = MailError(code: "noToken", shouldDisplay: true) public static let resourceError = MailError(code: "resourceError", shouldDisplay: true) public static let unknownToken = MailError(code: "unknownToken", shouldDisplay: true) - public static let noMailbox = MailError(code: "noMailbox", shouldDisplay: true) - public static let folderNotFound = MailError(code: "folderNotFound", shouldDisplay: true) + public static let noMailbox = MailError(code: "noMailbox") + public static let folderNotFound = MailError(code: "folderNotFound", + localizedDescription: MailResourcesStrings.Localizable.errorFolderNotFound, + shouldDisplay: true) public static let addressBookNotFound = MailError(code: "addressBookNotFound", shouldDisplay: true) public static let contactNotFound = MailError(code: "contactNotFound", shouldDisplay: true) - public static let messageNotFound = MailError(code: "messageNotFound", shouldDisplay: true) + public static let messageNotFound = MailError(code: "messageNotFound", + localizedDescription: MailResourcesStrings.Localizable.errorMessageNotFound, + shouldDisplay: true) public static let attachmentsSizeLimitReached = MailError(code: "attachmentsSizeLimitReached", localizedDescription: MailResourcesStrings.Localizable .attachmentFileLimitReached, diff --git a/MailResources/Localizable/de.lproj/Localizable.strings b/MailResources/Localizable/de.lproj/Localizable.strings index e913d235a0aff18e7cdbb3d8c9566eed967fa563..b2216640d1b4d469326746f3dfca37a84b3b6006 100644 GIT binary patch delta 470 zcmZ9IO-jQ+6vtn&BDfG;h=_=xA|fg#nrNc9ap6L&x~co5Nm^-1p&zJwZ!jLih2UO1 zfM-zg1U-TJdn1UH$NS*T{6A*izQ;cA%8!lGW>W^TA&z*2BUOpzmcA`6xx?g2B>NH& z)TjH>$(MP!nO*l;B;rd*&wvI@bpU%1_&_6oo~4P*MR|VWR63lbW*9DvJJOceOua9! z1z;lfId#`&Xn8!i)q<-S_+s<%I=5IwsYk@pU^;`bFUQn@6fjS}EX@>h1!I%FWAEf< zc)4P|s!n3V!(|Y5Qg6nD+{#|2_mi$i$jGwz381Hq_QzdXJdEfLq*L&OpvrzmcoL+L$J>e zu})*9wUy7VN+QF|&du&T^WJ=oi*N76m#f0|r3TuPQG^-Qm1)SiqY&RE3zh0X6^!`$ zTo`{^V;47L3g?QIaJFf!kTq%82N2Vm(inM6YO6D)jhb?9S`5h5qoWM^2rujJ#^}~I z9@ad$Nq?peanF0==@Ap1u+WoYWp-h-v|NXFhh@4TYrya8m^e_^*3MSuyS`$e{nVa5 zR)Y#!Q)td2R5#(b(H%iaI79FJft#>?s;;i_T=&F@@~OIgtOkMGiQ~4QI-<*CzS@43 n9d_fE$qYL;DxEluH~zNwUp2MKjOka#Yv{AJ-+%Eh4>kJ@(ehM4 delta 60 zcmcchlKIyY<_(#Qx}^-M3_1)73S6opTz-3VPui4jb*ky3?3Cz>dNP;jO0e1J5WYW+z{YLS8q7rwzjHwCvY z6e$XB-HOj3g5Yyl&x}jCoO_3xx!;`Jr)l}|W@geYtsG;3WjOE|4)zga$hr!TTI0z> zgiQnt=I2pq;YU4;=Nf{IHUvUe$Pq61+{h zXt7`5$YclS_)^y&i;aM+h`kQ;x>0EOKHt>VJVO(R)y~wiCHjn*6o~`AaDiRs0WzfO z{najK$kpvjP2G*>o7%T#w@vtToRBQ(*(V`n^^EAA>{|7Zlv66lB{Fr@va5YgwcCn|EzJG`269(~ delta 60 zcmZ42!m{Z<^M*`D-BN~B1|0?k1_K65AnD7H$WRHSjevY324f)0kimq(3W!0Z;pS((6t~%Ufc6+J0-ix| z=>a^68~v&m3L-^SS65fP*RMX7+4slnYLwX?E7g_))v!PvMS9?@tBNR zlzIDdXZhS_NFi3J7UzJ`CRvYxT>wHx+YHtW#?7ybm2=Ig_ko`&p}!390bbUz#=ImN zQBA1K-Noxh#n5|TL>dxzyth0<4OaVH)#HVVT=ytn>M(taMINUM%R;BbR8#fcbG?uY zbVa^^!>V_5#+tapeCdZPrV{h#2Fxw+Lh^_#gv%T~#?`n4=LVnYp7^We1Z>lmdL72r zqi2ex#^X0;n)*1p!i0f1Cj Ae*gdg delta 60 zcmcc6&T^-TWkWinZYe`5gARiNg8_pjko09pWT*tvMnJw1gE5e0$Y8=?1;ilIaPt(# G6aN8;-3(y> diff --git a/MailResources/Localizable/it.lproj/Localizable.strings b/MailResources/Localizable/it.lproj/Localizable.strings index 246d5e335f5a4bcd907fb8eb390f9b85152f18ce..bd1ebe7c8a07e42c2865a7ac08616ba1577f4485 100644 GIT binary patch delta 470 zcmZXQze)o^5Ql$3Da?zmNN&YC-KEi^HFCo}i z`2-d|fQ6NfPoSXq?G_fwFf+T$&i9*{x9^G9>&d%*p?adRR^%zfJT=wU6}hGWU*!}i z)`sdB@&2ap{AC_mrKO06jv_V4DW!Ez2?gr_B1&TlLyIxqQ+fJ8IqzNYxduwjqnRsu z-1`We6F*GwURka~i22tet~qZwYKW3;C~%C3STo*dsnSGwh_!Xh=@9lvd&IFab