Add crash reporter to secant (#531)

* [#525] Adds functions to configure, testCrash and check if it can start.

This adds a build phase where a dummy file is added to the project to
make the build and Plist copy happy. When building in the CI there
will be a script to replace this Plist file with the real one that
then will be copied to the bundle

Crashlytics will be "off" by default and then be turned on when
starting up to be an Opt-Out thing.

This is the only way it can be turned off later.
reference: https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=ios#enable_opt-in_reporting

The app will start with crash reporting turned off and will set it up
on by default on the application's code. Then if the user wants to
opt-out of crash reporting, it can. Otherwise, it won't be possible.

Adds opting out of crash reporting as a stored user preference.
This adds a value inside UserPreferencesStorage and its live and
mock counterparts.

also creates a builer for `CrashReporterClient` that has a Dependency
to `@Dependency(\.userStoredPreferences)` and sets the references
for the client to set the appropriate values into the user storage

`UserPreferencesStorage` as been adapted to be a TCA Dependency.

`SettingsStore` now as a `Toogle()` to turn off and on crash
reporting. But it doesn't work yet because I haven't found out
how to make a TCA Binding that can rely on an initial value that
is not hardcoded but injected from somewhere else.

See https://www.pointfree.co/episodes/ep158-safer-conciser-forms-part-1
https://www.pointfree.co/episodes/ep158-safer-conciser-forms-part-2

Adds Test Crash button and enable crash reporting

Adds upload-symbols run script phase

Closes #525

Add a custom build environment variable "UPLOAD_CRASHLYTICS_SYMBOLS"
that will let Xcode skip the upload_symbols script for debug builds

Fix Initialization tests

* bump build
This commit is contained in:
Francisco Gindre 2023-02-15 18:18:18 -03:00 committed by GitHub
parent f946de15bb
commit 26dd0ea988
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 487 additions and 38 deletions

5
.gitignore vendored
View File

@ -71,9 +71,10 @@ iOSInjectionProject/
#ignore Pods directory for example project
Pods
# do not commit Google Firebase config PLIST file
GoogleService-info.plist
# do not commit generated libraries to this repo
lib
*.a
*.generated.swift
env-vars.sh
wallet/wallet/Generated/Constants.generated.swift

View File

@ -12,8 +12,13 @@
0D185819272723FF0046B928 /* ColoredChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D185818272723FF0046B928 /* ColoredChip.swift */; };
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D18581A272728D60046B928 /* PhraseChip.swift */; };
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1C1AA227611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift */; };
0D26103A298C3DCD00CC9DE9 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 0D261039298C3DCD00CC9DE9 /* FirebaseCrashlytics */; };
0D26103C298C3E4800CC9DE9 /* CrashReportingInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D26103B298C3E4800CC9DE9 /* CrashReportingInterface.swift */; };
0D26103E298C3FA600CC9DE9 /* CrashReporterLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D26103D298C3FA600CC9DE9 /* CrashReporterLiveKey.swift */; };
0D261040298C406F00CC9DE9 /* CrashReporterTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D26103F298C406F00CC9DE9 /* CrashReporterTestKey.swift */; };
0D2ACE8026C2C67100D62E3C /* Zboto.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0D2ACE7F26C2C67100D62E3C /* Zboto.otf */; };
0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D35CC45277A36E00074316A /* ScrollableWhenScaled.swift */; };
0D3B01EC298DAF89007EBCDA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0D3B01EB298DAF89007EBCDA /* GoogleService-Info.plist */; };
0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */; };
0D3D040A2728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */; };
0D4E7A0926B364170058B01E /* SecantApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0826B364170058B01E /* SecantApp.swift */; };
@ -24,6 +29,7 @@
0D535FDF271F4214009A9E3E /* Rubik-VariableFont_wght.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0D535FDD271F4214009A9E3E /* Rubik-VariableFont_wght.ttf */; };
0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D535FE1271F9476009A9E3E /* EnumeratedChip.swift */; };
0D5D9B8F2914620700DBD03F /* URLRouting in Frameworks */ = {isa = PBXBuildFile; productRef = 0D5D9B8E2914620700DBD03F /* URLRouting */; };
0D63170029919970007D873F /* UserPreferencesStorageInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D6316FF29919970007D873F /* UserPreferencesStorageInterface.swift */; };
0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D6D628A276A528D002FB4CC /* DropDelegate.swift */; };
0D7CE63427349B5D0020E050 /* View+WhenDraggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */; };
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7DF08B271DCC0E00530046 /* ScreenBackground.swift */; };
@ -315,8 +321,12 @@
0D185818272723FF0046B928 /* ColoredChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColoredChip.swift; sourceTree = "<group>"; };
0D18581A272728D60046B928 /* PhraseChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhraseChip.swift; sourceTree = "<group>"; };
0D1C1AA227611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayReducerTests.swift; sourceTree = "<group>"; };
0D26103B298C3E4800CC9DE9 /* CrashReportingInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportingInterface.swift; sourceTree = "<group>"; };
0D26103D298C3FA600CC9DE9 /* CrashReporterLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReporterLiveKey.swift; sourceTree = "<group>"; };
0D26103F298C406F00CC9DE9 /* CrashReporterTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReporterTestKey.swift; sourceTree = "<group>"; };
0D2ACE7F26C2C67100D62E3C /* Zboto.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Zboto.otf; sourceTree = "<group>"; };
0D35CC45277A36E00074316A /* ScrollableWhenScaled.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollableWhenScaled.swift; sourceTree = "<group>"; };
0D3B01EB298DAF89007EBCDA /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
0D3D04072728B3440032ABC1 /* RecoveryPhraseDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayView.swift; sourceTree = "<group>"; };
0D3D04092728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseDisplayStore.swift; sourceTree = "<group>"; };
0D4E7A0526B364170058B01E /* secant-testnet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "secant-testnet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -333,6 +343,7 @@
0D535FDC271F4214009A9E3E /* Rubik-Italic-VariableFont_wght.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Rubik-Italic-VariableFont_wght.ttf"; sourceTree = "<group>"; };
0D535FDD271F4214009A9E3E /* Rubik-VariableFont_wght.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Rubik-VariableFont_wght.ttf"; sourceTree = "<group>"; };
0D535FE1271F9476009A9E3E /* EnumeratedChip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumeratedChip.swift; sourceTree = "<group>"; };
0D6316FF29919970007D873F /* UserPreferencesStorageInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageInterface.swift; sourceTree = "<group>"; };
0D6D628A276A528D002FB4CC /* DropDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDelegate.swift; sourceTree = "<group>"; };
0D7CE63327349B5D0020E050 /* View+WhenDraggable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+WhenDraggable.swift"; sourceTree = "<group>"; };
0D7DF08B271DCC0E00530046 /* ScreenBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenBackground.swift; sourceTree = "<group>"; };
@ -603,6 +614,7 @@
9E6612312878337F00C75B70 /* Lottie in Frameworks */,
0D5D9B8F2914620700DBD03F /* URLRouting in Frameworks */,
0DB4E0B42881FD9100947B78 /* ZcashLightClientKit in Frameworks */,
0D26103A298C3DCD00CC9DE9 /* FirebaseCrashlytics in Frameworks */,
9E2AC0FF27D8EC120042AA47 /* MnemonicSwift in Frameworks */,
6654C73A2715A38000901167 /* ComposableArchitecture in Frameworks */,
9EAB466D285A0468002904A0 /* Parsing in Frameworks */,
@ -766,6 +778,16 @@
path = Chips;
sourceTree = "<group>";
};
0D767873298C374F0047E085 /* CrashReporter */ = {
isa = PBXGroup;
children = (
0D26103B298C3E4800CC9DE9 /* CrashReportingInterface.swift */,
0D26103D298C3FA600CC9DE9 /* CrashReporterLiveKey.swift */,
0D26103F298C406F00CC9DE9 /* CrashReporterTestKey.swift */,
);
path = CrashReporter;
sourceTree = "<group>";
};
0D8A43C2272AEEA7005A6414 /* FontStyles */ = {
isa = PBXGroup;
children = (
@ -1267,6 +1289,7 @@
children = (
9E6612342878341F00C75B70 /* Lotties */,
0D4E7A0C26B364180058B01E /* Assets.xcassets */,
0D3B01EB298DAF89007EBCDA /* GoogleService-Info.plist */,
660558E8270C7A54009D6954 /* Colors.xcassets */,
9E37A2B727C8F59F00AE57B3 /* Localizable.strings */,
0D2ACE7E26C2C65E00D62E3C /* Fonts */,
@ -1315,6 +1338,7 @@
9EBDF978291F7E85000A1A05 /* AppVersion */,
9EBDF962291ECD42000A1A05 /* AudioServices */,
9EBDF969291ECEAC000A1A05 /* CaptureDevice */,
0D767873298C374F0047E085 /* CrashReporter */,
9EBDF94E291E5E5F000A1A05 /* DatabaseFiles */,
9EBDF959291E654F000A1A05 /* Deeplink */,
9EBDF971291F79C9000A1A05 /* DerivationTool */,
@ -1614,6 +1638,7 @@
9E3911442848EEB90073DD9A /* UserPreferencesStorage.swift */,
9EB863C62923C93B003D0F8B /* UserPreferencesStorageLive.swift */,
9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */,
0D6316FF29919970007D873F /* UserPreferencesStorageInterface.swift */,
);
path = UserPreferencesStorage;
sourceTree = "<group>";
@ -1834,11 +1859,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 0D4E7A2A26B364180058B01E /* Build configuration list for PBXNativeTarget "secant-testnet" */;
buildPhases = (
0D3B01ED298DB0FE007EBCDA /* ShellScript */,
664E39ED270C693C0044AD7E /* SwiftGen */,
6696BA8726F0B1D200D5C875 /* SwiftLint */,
0D4E7A0126B364170058B01E /* Sources */,
0D4E7A0226B364170058B01E /* Frameworks */,
0D4E7A0326B364170058B01E /* Resources */,
0D300FA72996EAF200576003 /* ShellScript */,
);
buildRules = (
);
@ -1852,6 +1879,7 @@
9E6612302878337F00C75B70 /* Lottie */,
0DB4E0B32881FD9100947B78 /* ZcashLightClientKit */,
0D5D9B8E2914620700DBD03F /* URLRouting */,
0D261039298C3DCD00CC9DE9 /* FirebaseCrashlytics */,
);
productName = secant;
productReference = 0D4E7A0526B364170058B01E /* secant-testnet.app */;
@ -1933,6 +1961,7 @@
9E66122F2878337F00C75B70 /* XCRemoteSwiftPackageReference "lottie-ios" */,
0DB4E0B22881FD9100947B78 /* XCRemoteSwiftPackageReference "ZcashLightClientKit" */,
0D5D9B8D2914620700DBD03F /* XCRemoteSwiftPackageReference "swift-url-routing" */,
0D261038298C3DCD00CC9DE9 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
);
productRefGroup = 0D4E7A0626B364170058B01E /* Products */;
projectDirPath = "";
@ -1969,6 +1998,7 @@
0D4E7A0D26B364180058B01E /* Assets.xcassets in Resources */,
0DACFA9727209FA70039EEA5 /* Roboto-Black.ttf in Resources */,
0DACFA9C27209FA70039EEA5 /* Roboto-ThinItalic.ttf in Resources */,
0D3B01EC298DAF89007EBCDA /* GoogleService-Info.plist in Resources */,
9E37A2B827C8F59F00AE57B3 /* Localizable.strings in Resources */,
0DACFA9627209FA70039EEA5 /* Roboto-Thin.ttf in Resources */,
0D2ACE8026C2C67100D62E3C /* Zboto.otf in Resources */,
@ -1992,6 +2022,43 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0D300FA72996EAF200576003 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 12;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif [[ $UPLOAD_CRASHLYTICS_SYMBOLS = \"NO\" ]]; then\n echo \"DEBUG BUILD: SKIPPING UPLOAD SYMBOLS STEP\"\nelse\n${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\nfi\n";
};
0D3B01ED298DB0FE007EBCDA /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Resources/GoogleService-Info.plist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/zsh;
shellScript = "# this creates an empty file for the firebase SDK\nCRASH_REPORTER_FILE=\"secant/Resources/GoogleService-Info.plist\"\nif [[ -f $CRASH_REPORTER_FILE ]]; then\n echo \"$CRASH_REPORTER_FILE Exists. Not doing anything.\"\nelse \n echo \"$CRASH_REPORTER_FILE does not exist. Will insert a DUMMY FILE\"\n\n echo \"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KICAgIDxrZXk+SVNfRFVNTVlfRklMRTwva2V5PgogICAgPHRydWU+PC90cnVlPgo8L2RpY3Q+CjwvcGxpc3Q+Cg==\" | base64 --decode > $CRASH_REPORTER_FILE\nfi\n";
};
664E39ED270C693C0044AD7E /* SwiftGen */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -2042,6 +2109,7 @@
files = (
2EB660E02747EAB900A06A07 /* OnboardingFlowView.swift in Sources */,
9E7FE0DF282D2DD600C374E8 /* ZcashBadge.swift in Sources */,
0D261040298C406F00CC9DE9 /* CrashReporterTestKey.swift in Sources */,
9EBDF975291F79F9000A1A05 /* DerivationToolInterface.swift in Sources */,
660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */,
9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */,
@ -2054,6 +2122,7 @@
9EBDF96E291ECED4000A1A05 /* CaptureDeviceLiveKey.swift in Sources */,
9EBDF968291ECDA2000A1A05 /* AudioServicesInterface.swift in Sources */,
9EB863BD2923C704003D0F8B /* NotificationCenterTest.swift in Sources */,
0D26103E298C3FA600CC9DE9 /* CrashReporterLiveKey.swift in Sources */,
9EB863A829239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift in Sources */,
2EDA07A027EDE18C00D6F09B /* TCATextField.swift in Sources */,
9EBDF961291E657B000A1A05 /* DeeplinkInterface.swift in Sources */,
@ -2097,6 +2166,7 @@
9E66122C2877188700C75B70 /* SyncStatusSnapshot.swift in Sources */,
9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */,
0DDB6A5127737D4A0012A410 /* RecoveryPhraseBackupFailedView.swift in Sources */,
0D63170029919970007D873F /* UserPreferencesStorageInterface.swift in Sources */,
9EBDF94D291D773A000A1A05 /* DiskSpaceCheckerMocks.swift in Sources */,
0D6D628B276A528E002FB4CC /* DropDelegate.swift in Sources */,
9EBDF986291F91EF000A1A05 /* LocalAuthenticationLiveKey.swift in Sources */,
@ -2222,6 +2292,7 @@
9EB863D02923D3FC003D0F8B /* SDKSynchronizerMocks.swift in Sources */,
9E612C7629880FC900D09B09 /* LogsHandlerTest.swift in Sources */,
2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */,
0D26103C298C3E4800CC9DE9 /* CrashReportingInterface.swift in Sources */,
F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */,
F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */,
9EB863952922D036003D0F8B /* NumberFormatterTestKey.swift in Sources */,
@ -2452,7 +2523,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 37;
CURRENT_PROJECT_VERSION = 39;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
ENABLE_BITCODE = NO;
@ -2469,6 +2541,7 @@
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG UNREDACTED";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
UPLOAD_CRASHLYTICS_SYMBOLS = NO;
};
name = Debug;
};
@ -2478,7 +2551,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 37;
CURRENT_PROJECT_VERSION = 39;
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
ENABLE_BITCODE = NO;
@ -2494,6 +2567,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
UPLOAD_CRASHLYTICS_SYMBOLS = YES;
};
name = Release;
};
@ -2626,6 +2700,14 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
0D261038298C3DCD00CC9DE9 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 9.6.0;
};
};
0D5D9B8D2914620700DBD03F /* XCRemoteSwiftPackageReference "swift-url-routing" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/pointfreeco/swift-url-routing";
@ -2677,6 +2759,11 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
0D261039298C3DCD00CC9DE9 /* FirebaseCrashlytics */ = {
isa = XCSwiftPackageProductDependency;
package = 0D261038298C3DCD00CC9DE9 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebaseCrashlytics;
};
0D5D9B8E2914620700DBD03F /* URLRouting */ = {
isa = XCSwiftPackageProductDependency;
package = 0D5D9B8D2914620700DBD03F /* XCRemoteSwiftPackageReference "swift-url-routing" */;

View File

@ -1,5 +1,23 @@
{
"pins" : [
{
"identity" : "abseil-cpp-swiftpm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/abseil-cpp-SwiftPM.git",
"state" : {
"revision" : "583de9bd60f66b40e78d08599cc92036c2e7e4e1",
"version" : "0.20220203.2"
}
},
{
"identity" : "boringssl-swiftpm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/boringssl-SwiftPM.git",
"state" : {
"revision" : "dd3eda2b05a3f459fc3073695ad1b28659066eab",
"version" : "0.9.1"
}
},
{
"identity" : "combine-schedulers",
"kind" : "remoteSourceControl",
@ -9,6 +27,51 @@
"version" : "0.9.1"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "7e80c25b51c2ffa238879b07fbfc5baa54bb3050",
"version" : "9.6.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "c1cfde8067668027b23a42c29d11c246152fe046",
"version" : "9.6.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "f6b558e3f801f2cac336b04f615ce111fa9ddaa0",
"version" : "9.2.1"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "0543562f85620b5b7c510c6bcbef75b562a5127b",
"version" : "7.11.0"
}
},
{
"identity" : "grpc-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/grpc/grpc-ios.git",
"state" : {
"revision" : "8440b914756e0d26d4f4d054a1c1581daedfc5b6",
"version" : "1.44.3-grpc"
}
},
{
"identity" : "grpc-swift",
"kind" : "remoteSourceControl",
@ -18,6 +81,24 @@
"version" : "1.14.0"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "5ccda3981422a84186387dbb763ba739178b529c",
"version" : "2.3.0"
}
},
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b",
"version" : "1.22.2"
}
},
{
"identity" : "lottie-ios",
"kind" : "remoteSourceControl",
@ -36,6 +117,24 @@
"version" : "2.2.4"
}
},
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
"revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
"version" : "2.30909.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "3e4e743631e86c8c70dbc6efdc7beaa6e90fd3bb",
"version" : "2.1.1"
}
},
{
"identity" : "sqlite.swift",
"kind" : "remoteSourceControl",

View File

@ -0,0 +1,41 @@
//
// CrashReporterLiveKey.swift
// secant-testnet
//
// Created by Francisco Gindre on 2/2/23.
//
import ComposableArchitecture
import FirebaseCore
import FirebaseCrashlytics
extension CrashReporterClient: DependencyKey {
static let liveValue: CrashReporterClient = CrashReporterClient(
configure: { canConfigure in
let fileName = "GoogleService-Info.plist"
// checks whether the crash reporter's config file is a dummy_file purposedly placed by the build job or the real one.
// this does not check the integrity of the Plist file for Firebase.
// that's a problem for the library itself.
guard
let configFile = Bundle.main.url(forResource: fileName, withExtension: nil),
let properties = NSDictionary(contentsOf: configFile),
properties["IS_DUMMY_FILE"] == nil,
canConfigure
else {
return
}
FirebaseApp.configure()
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)
},
testCrash: {
fatalError("Crash was triggered to test the crash reporter")
},
optIn: {
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)
},
optOut: {
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(false)
}
)
}

View File

@ -0,0 +1,16 @@
//
// CrashReporterTestKey.swift
// secant-testnet
//
// Created by Francisco Gindre on 2/2/23.
//
import ComposableArchitecture
extension CrashReporterClient: TestDependencyKey {
static let testValue: CrashReporterClient = CrashReporterClient(
configure: { _ in },
testCrash: {},
optIn: {},
optOut: {}
)
}

View File

@ -0,0 +1,31 @@
//
// CrashReportingInterface.swift
// secant-testnet
//
// Created by Francisco Gindre on 2/2/23.
//
import ComposableArchitecture
import Foundation
extension DependencyValues {
var crashReporter: CrashReporterClient {
get { self[CrashReporterClient.self] }
set { self[CrashReporterClient.self] = newValue }
}
}
struct CrashReporterClient {
/// Configures the crash reporter if possible.
/// if it can't be configured this will fail silently
var configure: (Bool) -> Void
/// this will test the crash reporter
/// - Note: depending of the crash reporter this may or may not crash your app.
var testCrash: () -> Void
/// this will tell the crash reporter that the user a has decided to opt-in crash reporting
var optIn: () -> Void
/// this will tell the crash reporter that the user has decided to opt-out of crash reporting
var optOut: () -> Void
}

View File

@ -17,6 +17,7 @@ struct UserPreferencesStorage {
case zcashFiatConverted
case zcashRecoveryPhraseTestCompleted
case zcashSessionAutoshielded
case zcashUserOptedOutOfCrashReporting
}
/// Default values for all preferences in case there is no value stored (counterparts to `Constants`)
@ -25,6 +26,7 @@ struct UserPreferencesStorage {
private let fiatConvertion: Bool
private let recoveryPhraseTestCompleted: Bool
private let sessionAutoshielded: Bool
private let userOptedOutOfCrashReporting: Bool
private let userDefaults: UserDefaultsClient
@ -34,6 +36,7 @@ struct UserPreferencesStorage {
fiatConvertion: Bool,
recoveryPhraseTestCompleted: Bool,
sessionAutoshielded: Bool,
userOptedOutOfCrashReporting: Bool,
userDefaults: UserDefaultsClient
) {
self.appSessionFrom = appSessionFrom
@ -41,6 +44,7 @@ struct UserPreferencesStorage {
self.fiatConvertion = fiatConvertion
self.recoveryPhraseTestCompleted = recoveryPhraseTestCompleted
self.sessionAutoshielded = sessionAutoshielded
self.userOptedOutOfCrashReporting = userOptedOutOfCrashReporting
self.userDefaults = userDefaults
}
@ -89,6 +93,15 @@ struct UserPreferencesStorage {
await setValue(bool, forKey: Constants.zcashSessionAutoshielded.rawValue)
}
/// Whether the user has opted out of crash reporting
var isUserOptedOutOfCrashReporting: Bool {
getValue(forKey: Constants.zcashUserOptedOutOfCrashReporting.rawValue, default: false)
}
func setIsUserOptedOutOfCrashReporting(_ bool: Bool) async {
await setValue(bool, forKey: Constants.zcashUserOptedOutOfCrashReporting.rawValue)
}
/// Use carefully: Deletes all user preferences from the User Defaults
func removeAll() async {
for key in Constants.allCases {

View File

@ -0,0 +1,38 @@
//
// UserPreferencesStorageInterface.swift
// secant-testnet
//
// Created by Francisco Gindre on 2/6/23.
//
import Foundation
import ComposableArchitecture
extension DependencyValues {
var userStoredPreferences: UserPreferencesStorageClient {
get { self [UserPreferencesStorageClient.self] }
set { self[UserPreferencesStorageClient.self] = newValue }
}
}
struct UserPreferencesStorageClient {
var activeAppSessionFrom: () -> TimeInterval
var setActiveAppSessionFrom: (TimeInterval) async -> Void
var currency: () -> String
var setCurrenty: (String) async -> Void
var isFiatConverted: () -> Bool
var setIsFiatConverted: (Bool) async -> Void
var isRecoveryPhraseTestCompleted: () -> Bool
var setIsRecoveryPhraseTestCompleted: (Bool) async -> Void
var isSessionAutoshielded: () -> Bool
var setIsSessionAutoshielded: (Bool) async -> Void
var isUserOptedOutOfCrashReporting: () -> Bool
var setIsUserOptedOutOfCrashReporting: (Bool) async -> Void
var removeAll: () async -> Void
}

View File

@ -6,6 +6,34 @@
//
import Foundation
import ComposableArchitecture
extension UserPreferencesStorageClient: DependencyKey {
static var liveValue: UserPreferencesStorageClient = {
let live = UserPreferencesStorage.live
return UserPreferencesStorageClient(
activeAppSessionFrom: { live.activeAppSessionFrom },
setActiveAppSessionFrom: live.setActiveAppSessionFrom(_:),
currency: { live.currency },
setCurrenty: live.setCurrency(_:),
isFiatConverted: { live.isFiatConverted },
setIsFiatConverted: live.setIsFiatConverted(_:),
isRecoveryPhraseTestCompleted: {
live.isRecoveryPhraseTestCompleted
},
setIsRecoveryPhraseTestCompleted: live.setIsRecoveryPhraseTestCompleted(_:),
isSessionAutoshielded: { live.isSessionAutoshielded },
setIsSessionAutoshielded: live.setIsSessionAutoshielded(_:),
isUserOptedOutOfCrashReporting: {
live.isUserOptedOutOfCrashReporting
},
setIsUserOptedOutOfCrashReporting: live.setIsUserOptedOutOfCrashReporting(_:),
removeAll: live.removeAll
)
}()
}
extension UserPreferencesStorage {
static let live = UserPreferencesStorage(
@ -14,6 +42,7 @@ extension UserPreferencesStorage {
fiatConvertion: true,
recoveryPhraseTestCompleted: false,
sessionAutoshielded: true,
userOptedOutOfCrashReporting: false,
userDefaults: .live()
)
}

View File

@ -6,6 +6,33 @@
//
import Foundation
import ComposableArchitecture
extension UserPreferencesStorageClient: TestDependencyKey {
static var testValue = {
let mock = UserPreferencesStorage.mock
return UserPreferencesStorageClient(
activeAppSessionFrom: { mock.activeAppSessionFrom },
setActiveAppSessionFrom: mock.setActiveAppSessionFrom(_:),
currency: { mock.currency },
setCurrenty: mock.setCurrency(_:),
isFiatConverted: { mock.isFiatConverted },
setIsFiatConverted: mock.setIsFiatConverted(_:),
isRecoveryPhraseTestCompleted: {
mock.isRecoveryPhraseTestCompleted
},
setIsRecoveryPhraseTestCompleted: mock.setIsRecoveryPhraseTestCompleted(_:),
isSessionAutoshielded: { mock.isSessionAutoshielded },
setIsSessionAutoshielded: mock.setIsSessionAutoshielded(_:),
isUserOptedOutOfCrashReporting: {
mock.isUserOptedOutOfCrashReporting
},
setIsUserOptedOutOfCrashReporting: mock.setIsUserOptedOutOfCrashReporting(_:),
removeAll: mock.removeAll
)
}()
}
extension UserPreferencesStorage {
static let mock = UserPreferencesStorage(
@ -14,6 +41,7 @@ extension UserPreferencesStorage {
fiatConvertion: true,
recoveryPhraseTestCompleted: false,
sessionAutoshielded: true,
userOptedOutOfCrashReporting: false,
userDefaults: .noOp
)
}

View File

@ -14,6 +14,7 @@ extension RootReducer {
case appDelegate(AppDelegateAction)
case checkBackupPhraseValidation
case checkWalletInitialization
case configureCrashReporter
case createNewWallet
case initializeSDK
case nukeWallet
@ -27,10 +28,13 @@ extension RootReducer {
case .initialization(.appDelegate(.didFinishLaunching)):
// TODO: [#524] finish all the wallet events according to definition, https://github.com/zcash/secant-ios-wallet/issues/524
LoggerProxy.event(".appDelegate(.didFinishLaunching)")
/// We need to fetch data from keychain, in order to be 100% sure the kecyhain can be read we delay the check a bit
return EffectTask(value: .initialization(.checkWalletInitialization))
.delay(for: 0.02, scheduler: mainQueue)
.eraseToEffect()
/// We need to fetch data from keychain, in order to be 100% sure the keychain can be read we delay the check a bit
return .concatenate(
EffectTask(value: .initialization(.configureCrashReporter)),
EffectTask(value: .initialization(.checkWalletInitialization))
.delay(for: 0.02, scheduler: mainQueue)
.eraseToEffect()
)
/// Evaluate the wallet's state based on keychain keys and database files presence
case .initialization(.checkWalletInitialization):
@ -193,6 +197,12 @@ extension RootReducer {
case .home, .destination, .onboarding, .phraseDisplay, .phraseValidation, .sandbox, .welcome:
return .none
case .initialization(.configureCrashReporter):
crashReporter.configure(
!userStoredPreferences.isUserOptedOutOfCrashReporting()
)
return .none
}
}
}

View File

@ -29,7 +29,8 @@ struct RootReducer: ReducerProtocol {
case sandbox(SandboxReducer.Action)
case welcome(WelcomeReducer.Action)
}
@Dependency(\.crashReporter) var crashReporter
@Dependency(\.databaseFiles) var databaseFiles
@Dependency(\.deeplink) var deeplink
@Dependency(\.derivationTool) var derivationTool
@ -37,6 +38,7 @@ struct RootReducer: ReducerProtocol {
@Dependency(\.mnemonic) var mnemonic
@Dependency(\.randomRecoveryPhrase) var randomRecoveryPhrase
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
@Dependency(\.userStoredPreferences) var userStoredPreferences
@Dependency(\.walletStorage) var walletStorage
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment

View File

@ -15,7 +15,9 @@ struct SettingsReducer: ReducerProtocol {
var isSharingLogs = false
var phraseDisplayState: RecoveryPhraseDisplayReducer.State
var rescanDialog: ConfirmationDialogState<SettingsReducer.Action>?
@BindableState var isCrashReportingOn: Bool
var tempSDKDir: URL {
let tempDir = FileManager.default.temporaryDirectory
let sdkFileName = "sdkLogs.txt"
@ -35,18 +37,21 @@ struct SettingsReducer: ReducerProtocol {
}
}
enum Action: Equatable {
enum Action: BindableAction, Equatable {
case backupWallet
case backupWalletAccessRequest
case binding(BindingAction<SettingsReducer.State>)
case cancelRescan
case exportLogs
case fullRescan
case logsExported
case logsShareFinished
case onAppear
case phraseDisplay(RecoveryPhraseDisplayReducer.Action)
case quickRescan
case rescanBlockchain
case updateDestination(SettingsReducer.State.Destination?)
case testCrashReporter // this will crash the app if live.
}
@Dependency(\.localAuthentication) var localAuthentication
@ -54,10 +59,15 @@ struct SettingsReducer: ReducerProtocol {
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
@Dependency(\.logsHandler) var logsHandler
@Dependency(\.walletStorage) var walletStorage
@Dependency(\.userStoredPreferences) var userStoredPreferences
@Dependency(\.crashReporter) var crashReporter
var body: some ReducerProtocol<State, Action> {
Reduce { state, action in
switch action {
case .onAppear:
state.isCrashReportingOn = !userStoredPreferences.isUserOptedOutOfCrashReporting()
return .none
case .backupWalletAccessRequest:
return .run { send in
if await localAuthentication.authenticate() {
@ -76,6 +86,17 @@ struct SettingsReducer: ReducerProtocol {
// TODO: [#221] - merge with issue 221 (https://github.com/zcash/secant-ios-wallet/issues/221) and its Error States
return .none
}
case .binding(\.$isCrashReportingOn):
if state.isCrashReportingOn {
crashReporter.optOut()
} else {
crashReporter.optIn()
}
return .run { [state] send in
await userStoredPreferences.setIsUserOptedOutOfCrashReporting(state.isCrashReportingOn)
}
case .cancelRescan, .quickRescan, .fullRescan:
state.rescanDialog = nil
@ -112,7 +133,7 @@ struct SettingsReducer: ReducerProtocol {
]
)
return .none
case .phraseDisplay:
state.destination = nil
return .none
@ -120,6 +141,13 @@ struct SettingsReducer: ReducerProtocol {
case .updateDestination(let destination):
state.destination = destination
return .none
case .testCrashReporter:
crashReporter.testCrash()
return .none
case .binding:
return .none
}
}
@ -164,7 +192,8 @@ extension SettingsReducer.State {
static let placeholder = SettingsReducer.State(
phraseDisplayState: RecoveryPhraseDisplayReducer.State(
phrase: .placeholder
)
),
isCrashReportingOn: true
)
}

View File

@ -6,14 +6,16 @@ struct SettingsView: View {
var body: some View {
WithViewStore(store) { viewStore in
VStack {
VStack(spacing: 40) {
Toggle("Enable Crash Reporting",
isOn: viewStore.binding(\.$isCrashReportingOn)
)
Button(
action: { viewStore.send(.backupWalletAccessRequest) },
label: { Text("Backup Wallet") }
)
.activeButtonStyle
.frame(height: 50)
.padding(30)
Button(
action: { viewStore.send(.rescanBlockchain) },
@ -21,7 +23,6 @@ struct SettingsView: View {
)
.primaryButtonStyle
.frame(height: 50)
.padding(.horizontal, 30)
Button(
action: { viewStore.send(.exportLogs) },
@ -38,12 +39,17 @@ struct SettingsView: View {
)
.primaryButtonStyle
.frame(height: 50)
.padding(.horizontal, 30)
.padding(.top, 30)
.disabled(viewStore.exportLogsDisabled)
Button(
action: { viewStore.send(.testCrashReporter) },
label: { Text("Test Crash Reporter") }
)
.primaryButtonStyle
.frame(height: 50)
Spacer()
}
.padding(.horizontal, 30)
.navigationTitle("Settings")
.applyScreenBackground()
.confirmationDialog(
@ -56,6 +62,7 @@ struct SettingsView: View {
RecoveryPhraseDisplayView(store: store.backupPhraseStore())
}
)
.onAppear { viewStore.send(.onAppear) }
if viewStore.isSharingLogs {
UIShareDialogView(

View File

@ -86,12 +86,7 @@
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>FirebaseCrashlyticsCollectionEnabled</key>
<false/>
</dict>
</plist>

View File

@ -105,16 +105,19 @@ class AppInitializationTests: XCTestCase {
await testScheduler.advance(by: 0.02)
// ad 1.
await store.receive(.initialization(.checkWalletInitialization))
await store.receive(.initialization(.configureCrashReporter))
// ad 2.
await store.receive(.initialization(.respondToWalletInitializationState(.initialized)))
await store.receive(.initialization(.checkWalletInitialization))
// ad 3.
await store.receive(.initialization(.respondToWalletInitializationState(.initialized)))
// ad 4.
await store.receive(.initialization(.initializeSDK)) { state in
state.storedWallet = .placeholder
}
// ad 4.
// ad 5.
await store.receive(.initialization(.checkBackupPhraseValidation)) { state in
state.appInitializationState = .initialized
}
@ -153,9 +156,12 @@ class AppInitializationTests: XCTestCase {
testScheduler.advance(by: 0.02)
// ad 1.
store.receive(.initialization(.configureCrashReporter))
// ad 2
store.receive(.initialization(.checkWalletInitialization))
// ad 2.
// ad 3.
store.receive(.initialization(.respondToWalletInitializationState(.keysMissing))) { state in
state.appInitializationState = .keysMissing
}
@ -184,14 +190,17 @@ class AppInitializationTests: XCTestCase {
// the 0.02 delay ensures keychain is ready
// the 3.0 delay ensures the welcome screen is visible till the initialization check is done
testScheduler.advance(by: 3.02)
// ad 1.
store.receive(.initialization(.checkWalletInitialization))
store.receive(.initialization(.configureCrashReporter))
// ad 2.
store.receive(.initialization(.checkWalletInitialization))
// ad 3.
store.receive(.initialization(.respondToWalletInitializationState(.uninitialized)))
// ad 3.
// ad 4.
store.receive(.destination(.updateDestination(.onboarding))) { state in
state.destinationState.previousDestination = .welcome
state.destinationState.internalDestination = .onboarding

View File

@ -45,7 +45,10 @@ class SettingsTests: XCTestCase {
)
let store = TestStore(
initialState: SettingsReducer.State(phraseDisplayState: RecoveryPhraseDisplayReducer.State(phrase: nil)),
initialState: SettingsReducer.State(
phraseDisplayState: RecoveryPhraseDisplayReducer.State(phrase: nil),
isCrashReportingOn: false
),
reducer: SettingsReducer()
) { dependencies in
dependencies.localAuthentication = .mockAuthenticationSucceeded
@ -109,7 +112,8 @@ class SettingsTests: XCTestCase {
.default(TextState("Full rescan"), action: .send(.fullRescan)),
.cancel(TextState("Cancel"))
]
)
),
isCrashReportingOn: false
),
reducer: SettingsReducer()
)
@ -132,7 +136,8 @@ class SettingsTests: XCTestCase {
.default(TextState("Full rescan"), action: .send(.fullRescan)),
.cancel(TextState("Cancel"))
]
)
),
isCrashReportingOn: false
),
reducer: SettingsReducer()
)
@ -155,7 +160,8 @@ class SettingsTests: XCTestCase {
.default(TextState("Full rescan"), action: .send(.fullRescan)),
.cancel(TextState("Cancel"))
]
)
),
isCrashReportingOn: false
),
reducer: SettingsReducer()
)
@ -178,7 +184,8 @@ class SettingsTests: XCTestCase {
.default(TextState("Full rescan"), action: .send(.fullRescan)),
.cancel(TextState("Cancel"))
]
)
),
isCrashReportingOn: false
),
reducer: SettingsReducer()
)
@ -209,7 +216,8 @@ class SettingsTests: XCTestCase {
.default(TextState("Full rescan"), action: .send(.fullRescan)),
.cancel(TextState("Cancel"))
]
)
),
isCrashReportingOn: false
),
reducer: SettingsReducer()
)

View File

@ -26,6 +26,7 @@ class UserPreferencesStorageTests: XCTestCase {
fiatConvertion: true,
recoveryPhraseTestCompleted: true,
sessionAutoshielded: false,
userOptedOutOfCrashReporting: true,
userDefaults: .live(userDefaults: userDefaults)
)
await storage.removeAll()
@ -107,6 +108,7 @@ class UserPreferencesStorageTests: XCTestCase {
fiatConvertion: true,
recoveryPhraseTestCompleted: true,
sessionAutoshielded: false,
userOptedOutOfCrashReporting: true,
userDefaults: mockedUD
)
@ -127,6 +129,7 @@ class UserPreferencesStorageTests: XCTestCase {
fiatConvertion: true,
recoveryPhraseTestCompleted: true,
sessionAutoshielded: false,
userOptedOutOfCrashReporting: true,
userDefaults: mockedUD
)
@ -147,6 +150,7 @@ class UserPreferencesStorageTests: XCTestCase {
fiatConvertion: true,
recoveryPhraseTestCompleted: true,
sessionAutoshielded: false,
userOptedOutOfCrashReporting: true,
userDefaults: mockedUD
)
@ -167,6 +171,7 @@ class UserPreferencesStorageTests: XCTestCase {
fiatConvertion: true,
recoveryPhraseTestCompleted: true,
sessionAutoshielded: false,
userOptedOutOfCrashReporting: true,
userDefaults: mockedUD
)
@ -187,6 +192,7 @@ class UserPreferencesStorageTests: XCTestCase {
fiatConvertion: true,
recoveryPhraseTestCompleted: true,
sessionAutoshielded: false,
userOptedOutOfCrashReporting: true,
userDefaults: mockedUD
)