From 00685d7ce925e841cd749f892013bc163396a72d Mon Sep 17 00:00:00 2001 From: Marten Rebane Date: Wed, 11 Feb 2026 01:23:14 +0200 Subject: [PATCH] Add Firebase Crashlytics support --- .gitignore | 3 +- RIADigiDoc.xcodeproj/project.pbxproj | 56 +++++++++ .../xcshareddata/swiftpm/Package.resolved | 119 +++++++++++++++++- RIADigiDoc/DI/AppContainer.swift | 10 ++ .../Model/CrashReport/CrashReportDialog.swift | 24 ++++ RIADigiDoc/Domain/Preferences/DataStore.swift | 10 ++ .../Preferences/DataStoreProtocol.swift | 4 + RIADigiDoc/LibrarySetup.swift | 5 + RIADigiDoc/RIADigiDocApp.swift | 23 ++++ .../Supporting files/Localizable.xcstrings | 90 +++++++++++++ .../CrashReport/CrashReportManager.swift | 75 +++++++++++ .../CrashReportManagerProtocol.swift | 29 +++++ codemagic.yaml | 2 +- 13 files changed, 447 insertions(+), 3 deletions(-) create mode 100644 RIADigiDoc/Domain/Model/CrashReport/CrashReportDialog.swift create mode 100644 RIADigiDoc/Util/Manager/CrashReport/CrashReportManager.swift create mode 100644 RIADigiDoc/Util/Manager/CrashReport/CrashReportManagerProtocol.swift diff --git a/.gitignore b/.gitignore index 3bc301d8..17910289 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ xcuserdata **/Mocks/** # Xcode-Build-Server -buildServer.json \ No newline at end of file +buildServer.json +RIADigiDoc/Supporting files/GoogleService-Info.plist diff --git a/RIADigiDoc.xcodeproj/project.pbxproj b/RIADigiDoc.xcodeproj/project.pbxproj index cc8b2ce8..1002dc07 100644 --- a/RIADigiDoc.xcodeproj/project.pbxproj +++ b/RIADigiDoc.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 1A4A77602EF1983F001747D8 /* ConfigLib in Frameworks */ = {isa = PBXBuildFile; productRef = 1A4A775F2EF1983F001747D8 /* ConfigLib */; }; 1A4A77622EF1983F001747D8 /* ConfigLibMocks in Frameworks */ = {isa = PBXBuildFile; productRef = 1A4A77612EF1983F001747D8 /* ConfigLibMocks */; }; 1A5026ED2E8BFCF400C184D7 /* IdCardLib in Frameworks */ = {isa = PBXBuildFile; productRef = 1A5026EC2E8BFCF400C184D7 /* IdCardLib */; }; + DF1D595A2F3BF88000855E2E /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = DF1D59592F3BF88000855E2E /* FirebaseCore */; }; + DF1D595C2F3BF88000855E2E /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = DF1D595B2F3BF88000855E2E /* FirebaseCrashlytics */; }; DF21D3592E8AA98E00A8E28C /* CommonsTestShared in Frameworks */ = {isa = PBXBuildFile; productRef = DFAABD0A2E82B45700906874 /* CommonsTestShared */; }; DF3D9BB22E8355F9009B086E /* CommonsTestShared in Frameworks */ = {isa = PBXBuildFile; productRef = DF0B681B2D0779EF00A5457C /* CommonsTestShared */; }; DF3E4A7F2CE269C900137235 /* UtilsLib in Frameworks */ = {isa = PBXBuildFile; productRef = DF3E4A7E2CE269C900137235 /* UtilsLib */; }; @@ -198,6 +200,8 @@ Util/File/FileInspectorProtocol.swift, Util/Language/LanguageSettings.swift, Util/Language/LanguageSettingsProtocol.swift, + Util/Manager/CrashReport/CrashReportManager.swift, + Util/Manager/CrashReport/CrashReportManagerProtocol.swift, Util/Proxy/ProxyUtil.swift, Util/Proxy/ProxyUtilProtocol.swift, Util/Recipient/RecipientUtil.swift, @@ -445,7 +449,9 @@ DF54F82F2D431BD50021D05A /* X509 in Frameworks */, 1A5026ED2E8BFCF400C184D7 /* IdCardLib in Frameworks */, DFA23CBD2EB6DC64000403E4 /* MobileIdLib in Frameworks */, + DF1D595C2F3BF88000855E2E /* FirebaseCrashlytics in Frameworks */, DF3E922D2D2C371200F2990A /* Alamofire in Frameworks */, + DF1D595A2F3BF88000855E2E /* FirebaseCore in Frameworks */, DF3E922B2D285A1D00F2990A /* ConfigLib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -633,6 +639,7 @@ DFED7A602CC9A42A00D8BCA9 /* SwiftLint */, DFD24C3B2CD1B718004C1D2E /* Embed Frameworks */, DF4E43CC2D0BA38600967997 /* Embed Foundation Extensions */, + DF1D59572F3BF3F800855E2E /* Setup Firebase Crashlytics */, ); buildRules = ( ); @@ -655,6 +662,8 @@ 1A5026EC2E8BFCF400C184D7 /* IdCardLib */, DFA23CBC2EB6DC64000403E4 /* MobileIdLib */, DF64D0ED2EC36F7700FF73C6 /* SmartIdLib */, + DF1D59592F3BF88000855E2E /* FirebaseCore */, + DF1D595B2F3BF88000855E2E /* FirebaseCrashlytics */, ); productName = RIADigiDoc; productReference = DFDB147B2CC97B0E00153876 /* RIADigiDoc.app */; @@ -770,6 +779,7 @@ 1A7CC1C52E71CAAB0002CEA3 /* XCRemoteSwiftPackageReference "ASN1Decoder" */, 1A2D04BC2E85850500A7269B /* XCRemoteSwiftPackageReference "BigInt" */, 1A2D04BD2E8588F100A7269B /* XCRemoteSwiftPackageReference "SwiftECC" */, + DF1D59582F3BF88000855E2E /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, ); preferredProjectObjectVersion = 77; productRefGroup = DFDB147C2CC97B0E00153876 /* Products */; @@ -830,6 +840,30 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + DF1D59572F3BF3F800855E2E /* Setup Firebase Crashlytics */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}", + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${PRODUCT_NAME}", + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist", + "$(TARGET_BUILD_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/GoogleService-Info.plist", + "$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)", + ); + name = "Setup Firebase Crashlytics"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\"\n"; + }; DFED7A602CC9A42A00D8BCA9 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -1170,6 +1204,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = "\"RIADigiDoc/Preview Content\""; DEVELOPMENT_TEAM = ET847QJV9F; ENABLE_APP_SANDBOX = YES; @@ -1200,6 +1235,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; + OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = ee.ria.digidoc; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1226,6 +1262,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = "\"RIADigiDoc/Preview Content\""; DEVELOPMENT_TEAM = ET847QJV9F; ENABLE_APP_SANDBOX = YES; @@ -1256,6 +1293,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; + OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = ee.ria.digidoc; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1462,6 +1500,14 @@ minimumVersion = 1.10.0; }; }; + DF1D59582F3BF88000855E2E /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; + requirement = { + kind = exactVersion; + version = 12.9.0; + }; + }; DF54F82D2D431BD50021D05A /* XCRemoteSwiftPackageReference "swift-certificates" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-certificates.git"; @@ -1521,6 +1567,16 @@ isa = XCSwiftPackageProductDependency; productName = CommonsTestShared; }; + DF1D59592F3BF88000855E2E /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = DF1D59582F3BF88000855E2E /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; + DF1D595B2F3BF88000855E2E /* FirebaseCrashlytics */ = { + isa = XCSwiftPackageProductDependency; + package = DF1D59582F3BF88000855E2E /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCrashlytics; + }; DF3E4A7E2CE269C900137235 /* UtilsLib */ = { isa = XCSwiftPackageProductDependency; productName = UtilsLib; diff --git a/RIADigiDoc.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RIADigiDoc.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9ac97b40..a9cd293e 100644 --- a/RIADigiDoc.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RIADigiDoc.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,6 +1,15 @@ { - "originHash" : "5cfdacc6dc3e2f080c69617c473a32e3cc20075d8db1ab8dee4ee4d5581b33b0", + "originHash" : "fa6c8e68b36c2e335a696f6d8cf861f1af83c408d029f7245a5bcdda75ca76ff", "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5", + "version" : "1.2024072200.0" + } + }, { "identity" : "alamofire", "kind" : "remoteSourceControl", @@ -10,6 +19,15 @@ "version" : "5.10.2" } }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "61b85103a1aeed8218f17c794687781505fbbef5", + "version" : "11.2.0" + } + }, { "identity" : "asn1", "kind" : "remoteSourceControl", @@ -55,6 +73,105 @@ "version" : "2.5.3" } }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk.git", + "state" : { + "revision" : "9b3aed4fa6226125305b82d4d86c715bef250785", + "version" : "12.9.0" + } + }, + { + "identity" : "google-ads-on-device-conversion-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk", + "state" : { + "revision" : "35b601a60fbbea2de3ea461f604deaaa4d8bbd0c", + "version" : "3.2.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "2ffd220823f3716904733162e9ae685545c276d1", + "version" : "12.8.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "617af071af9aa1d6a091d59a202910ac482128f9", + "version" : "10.1.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "60da361632d0de02786f709bdc0c4df340f7613e", + "version" : "8.1.0" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "75b31c842f664a0f46a2e590a570e370249fd8f6", + "version" : "1.69.1" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "fb7f2740b1570d2f7599c6bb9531bf4fad6974b7", + "version" : "5.0.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe", + "version" : "101.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, { "identity" : "swift-asn1", "kind" : "remoteSourceControl", diff --git a/RIADigiDoc/DI/AppContainer.swift b/RIADigiDoc/DI/AppContainer.swift index 9c97616b..d1ecdff3 100644 --- a/RIADigiDoc/DI/AppContainer.swift +++ b/RIADigiDoc/DI/AppContainer.swift @@ -534,4 +534,14 @@ extension Container { ) } } + + @MainActor + var crashReportManager: Factory { + self { @MainActor in + CrashReportManager( + dataStore: self.dataStore() + ) + } + .singleton + } } diff --git a/RIADigiDoc/Domain/Model/CrashReport/CrashReportDialog.swift b/RIADigiDoc/Domain/Model/CrashReport/CrashReportDialog.swift new file mode 100644 index 00000000..48193adc --- /dev/null +++ b/RIADigiDoc/Domain/Model/CrashReport/CrashReportDialog.swift @@ -0,0 +1,24 @@ +/* + * Copyright 2017 - 2025 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import Foundation + +struct CrashReportDialog: Sendable, Identifiable { + let id = UUID() +} diff --git a/RIADigiDoc/Domain/Preferences/DataStore.swift b/RIADigiDoc/Domain/Preferences/DataStore.swift index 88f02827..9fe35f53 100644 --- a/RIADigiDoc/Domain/Preferences/DataStore.swift +++ b/RIADigiDoc/Domain/Preferences/DataStore.swift @@ -502,6 +502,15 @@ public actor DataStore: DataStoreProtocol { userDefaults().set(isSaved, forKey: Keys.isLogFileSaved) } + // MARK: - Crashlytics + public func getIsCrashlyticsAlwaysEnabled() async -> Bool { + userDefaults().bool(forKey: Keys.isCrashlyticsAlwaysEnabled) + } + + public func setIsCrashlyticsAlwaysEnabled(_ isAlwaysEnabled: Bool) async { + userDefaults().set(isAlwaysEnabled, forKey: Keys.isCrashlyticsAlwaysEnabled) + } + // MARK: - Constants private enum DefaultValues { @@ -569,5 +578,6 @@ public actor DataStore: DataStoreProtocol { static let enableLoggingNextSession = "enableLoggingNextSession" static let enableLoggingThisSession = "enableLoggingThisSession" static let isLogFileSaved = "isLogFileSaved" + static let isCrashlyticsAlwaysEnabled = "isCrashlyticsAlwaysEnabled" } } diff --git a/RIADigiDoc/Domain/Preferences/DataStoreProtocol.swift b/RIADigiDoc/Domain/Preferences/DataStoreProtocol.swift index fc7f4b2f..9e6ef159 100644 --- a/RIADigiDoc/Domain/Preferences/DataStoreProtocol.swift +++ b/RIADigiDoc/Domain/Preferences/DataStoreProtocol.swift @@ -118,4 +118,8 @@ public protocol DataStoreProtocol: Sendable { func setEnableLoggingThisSession(_ isEnabled: Bool) async func getIsLogFileSaved() async -> Bool func setIsLogFileSaved(_ isSaved: Bool) async + + // MARK: - Crashlytics + func getIsCrashlyticsAlwaysEnabled() async -> Bool + func setIsCrashlyticsAlwaysEnabled(_ isEnabled: Bool) async } diff --git a/RIADigiDoc/LibrarySetup.swift b/RIADigiDoc/LibrarySetup.swift index d4c48817..ffc831b0 100644 --- a/RIADigiDoc/LibrarySetup.swift +++ b/RIADigiDoc/LibrarySetup.swift @@ -19,6 +19,8 @@ import Foundation import FactoryKit +import Firebase +import FirebaseCrashlytics import LibdigidocLibSwift import CryptoObjCWrapper import CryptoSwift @@ -68,6 +70,9 @@ actor LibrarySetup: Loggable { func setupLibraries() async { let isLoggingEnabled = await initializeLogging() + FirebaseApp.configure() + Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(false) + do { let proxyInfo = await proxyUtil.getProxyInfo() let appLanguage = await dataStore.getSelectedLanguage() diff --git a/RIADigiDoc/RIADigiDocApp.swift b/RIADigiDoc/RIADigiDocApp.swift index 37ed00cb..ce2cfdc3 100644 --- a/RIADigiDoc/RIADigiDocApp.swift +++ b/RIADigiDoc/RIADigiDocApp.swift @@ -28,6 +28,7 @@ import CommonsLib struct RIADigiDocApp: App { @State private var languageSettings: LanguageSettings @State private var themeSettings: ThemeSettings + @State private var crashReportManager: CrashReportManager @State private var isSetupComplete = false @State private var isJailbroken: Bool = false @@ -45,6 +46,7 @@ struct RIADigiDocApp: App { init() { _languageSettings = State(wrappedValue: Container.shared.languageSettings()) _themeSettings = State(wrappedValue: Container.shared.themeSettings()) + _crashReportManager = State(wrappedValue: Container.shared.crashReportManager()) self.configurationProperty = Container.shared.configurationProperty() self.configurationLoader = Container.shared.configurationLoader() @@ -87,6 +89,27 @@ struct RIADigiDocApp: App { ContentView() .environment(pathManager) .appNavigation(pathManager: pathManager) + .task { + await crashReportManager.evaluateCrashReporting() + } + .alert( + languageSettings.localized("Crash report title"), + isPresented: $crashReportManager.showCrashDialog + ) { + Button(languageSettings.localized("Crash report send")) { + crashReportManager.sendReport() + } + Button(languageSettings.localized("Crash report always send")) { + Task { + await crashReportManager.alwaysSendReport() + } + } + Button(languageSettings.localized("Crash report dont send"), role: .cancel) { + crashReportManager.doNotSendReport() + } + } message: { + Text(verbatim: languageSettings.localized("Crash report message")) + } } else { InitView() .environment(pathManager) diff --git a/RIADigiDoc/Supporting files/Localizable.xcstrings b/RIADigiDoc/Supporting files/Localizable.xcstrings index f43579bc..6cbef3c0 100644 --- a/RIADigiDoc/Supporting files/Localizable.xcstrings +++ b/RIADigiDoc/Supporting files/Localizable.xcstrings @@ -1002,6 +1002,96 @@ } } }, + "Crash report always send" : { + "comment" : "Crash report dialog always send reports button", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always send" + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saada alati" + } + } + } + }, + "Crash report dont send" : { + "comment" : "Crash report dialog don’t send report button", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Don’t send" + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ära saada" + } + } + } + }, + "Crash report message" : { + "comment" : "Crash report dialog message", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Application crashed. Please send us crash report, so we can fix the problem." + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rakenduses juhtus viga. Palun saada meile vearaport, et saaksime selle vea parandada." + } + } + } + }, + "Crash report send" : { + "comment" : "Crash report dialog send report button", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send" + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saada" + } + } + } + }, + "Crash report title" : { + "comment" : "Crash report dialog title", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send crash report?" + } + }, + "et" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kas saata vearaport?" + } + } + } + }, "Crypto files encrypted" : { "comment" : "Crypto container files names locked", "extractionState" : "manual", diff --git a/RIADigiDoc/Util/Manager/CrashReport/CrashReportManager.swift b/RIADigiDoc/Util/Manager/CrashReport/CrashReportManager.swift new file mode 100644 index 00000000..fff7959a --- /dev/null +++ b/RIADigiDoc/Util/Manager/CrashReport/CrashReportManager.swift @@ -0,0 +1,75 @@ +/* + * Copyright 2017 - 2025 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import Foundation +import FirebaseCrashlytics +import UtilsLib + +@MainActor +@Observable +final class CrashReportManager: CrashReportManagerProtocol, Loggable { + + private let dataStore: DataStoreProtocol + + var showCrashDialog = false + + init( + dataStore: DataStoreProtocol + ) { + self.dataStore = dataStore + } + + func evaluateCrashReporting() async { + if await dataStore.getIsCrashlyticsAlwaysEnabled() { + checkForUnsentReports(send: true) + } else { + showCrashDialog = true + } + } + + func sendReport() { + CrashReportManager.logger().info("Sending crash report") + checkForUnsentReports(send: true) + showCrashDialog = false + } + + func alwaysSendReport() async { + CrashReportManager.logger().info("(Always) sending crash report") + await dataStore.setIsCrashlyticsAlwaysEnabled(true) + checkForUnsentReports(send: true) + showCrashDialog = false + } + + func doNotSendReport() { + CrashReportManager.logger().info("Not sending crash report") + checkForUnsentReports(send: false) + showCrashDialog = false + } + + private func checkForUnsentReports(send: Bool) { + Crashlytics.crashlytics().checkForUnsentReports { hasUnsentReports in + CrashReportManager.logger().info("Has unsent crash reports: \(hasUnsentReports)") + if send && hasUnsentReports { + Crashlytics.crashlytics().sendUnsentReports() + } else { + Crashlytics.crashlytics().deleteUnsentReports() + } + } + } +} diff --git a/RIADigiDoc/Util/Manager/CrashReport/CrashReportManagerProtocol.swift b/RIADigiDoc/Util/Manager/CrashReport/CrashReportManagerProtocol.swift new file mode 100644 index 00000000..0cc99d61 --- /dev/null +++ b/RIADigiDoc/Util/Manager/CrashReport/CrashReportManagerProtocol.swift @@ -0,0 +1,29 @@ +/* + * Copyright 2017 - 2025 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import Foundation + +/// @mockable +@MainActor +protocol CrashReportManagerProtocol: Sendable { + func evaluateCrashReporting() async + func sendReport() + func alwaysSendReport() async + func doNotSendReport() +} diff --git a/codemagic.yaml b/codemagic.yaml index 72e0b4db..c71e4c8f 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -6,7 +6,7 @@ scripts: - &get_google_services_plist name: "Get Google Services configuration" script: | - echo $GOOGLE_SERVICES_PLIST | base64 --decode > $CM_BUILD_DIR/RIADigiDoc/GoogleService-Info.plist + echo $GOOGLE_SERVICES_PLIST | base64 --decode > "$CM_BUILD_DIR/RIADigiDoc/Supporting files/GoogleService-Info.plist" workflows: debug: