[#490] Consolidation of TCA dependencies - 2nd batch (#491)

- all dependencies are now consolidated
- cleanup of dependencies
- getting rid of all .live instances in the tests
- all dependencies use mocked or noOp instances
This commit is contained in:
Lukas Korba 2022-11-17 12:25:55 +01:00 committed by GitHub
parent 7839d56977
commit c8b97a8266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
99 changed files with 1974 additions and 1575 deletions

View File

@ -68,7 +68,7 @@
2EDA07A027EDE18C00D6F09B /* TCATextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA079F27EDE18C00D6F09B /* TCATextField.swift */; };
2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */; };
2EDA07A427EDE2A900D6F09B /* DebugFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */; };
34429C6E28E703CD00F2B929 /* TransactionSendingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34429C6D28E703CD00F2B929 /* TransactionSendingTests.swift */; };
34429C6E28E703CD00F2B929 /* TransactionSendingSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34429C6D28E703CD00F2B929 /* TransactionSendingSnapshotTests.swift */; };
3448CB3228E47666006ADEDB /* NotEnoughFreeSpaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448CB3128E47666006ADEDB /* NotEnoughFreeSpaceView.swift */; };
3448CB3728E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448CB3628E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift */; };
346715A528E2027D0035F7C4 /* CheckCircleStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346715A428E2027D0035F7C4 /* CheckCircleStore.swift */; };
@ -94,12 +94,23 @@
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */; };
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */; };
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */; };
9E01F8242833C0D8000EFC57 /* WrappedURIParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E01F8232833C0D8000EFC57 /* WrappedURIParser.swift */; };
9E01F8282833CDA0000EFC57 /* ScanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E01F8272833CDA0000EFC57 /* ScanTests.swift */; };
9E02B56A27FED43E005B809B /* WrappedFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56927FED43E005B809B /* WrappedFileManager.swift */; };
9E02B56A27FED43E005B809B /* FileManagerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56927FED43E005B809B /* FileManagerInterface.swift */; };
9E02B56C27FED475005B809B /* DatabaseFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */; };
9E153A5F2920CE2700112F41 /* MnemonicInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5E2920CD5100112F41 /* MnemonicInterface.swift */; };
9E153A602920CE2700112F41 /* MnemonicLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5C2920CD5100112F41 /* MnemonicLiveKey.swift */; };
9E153A612920CE2700112F41 /* MnemonicMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5B2920CD5100112F41 /* MnemonicMocks.swift */; };
9E153A622920CE2700112F41 /* MnemonicTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5D2920CD5100112F41 /* MnemonicTestKey.swift */; };
9E153A6729210B3B00112F41 /* PasteboardLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A6429210B3B00112F41 /* PasteboardLiveKey.swift */; };
9E153A6829210B3B00112F41 /* PasteboardInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A6529210B3B00112F41 /* PasteboardInterface.swift */; };
9E153A6929210B3B00112F41 /* PasteboardTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A6629210B3B00112F41 /* PasteboardTestKey.swift */; };
9E153A6E292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A6B292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift */; };
9E153A6F292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A6C292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift */; };
9E153A70292167FF00112F41 /* ZcashSDKEnvironmentInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A6D292167FF00112F41 /* ZcashSDKEnvironmentInterface.swift */; };
9E153A7529216EFB00112F41 /* UserDefaultsLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A7229216EFB00112F41 /* UserDefaultsLiveKey.swift */; };
9E153A7629216EFB00112F41 /* UserDefaultsInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A7329216EFB00112F41 /* UserDefaultsInterface.swift */; };
9E153A7729216EFB00112F41 /* UserDefaultsTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A7429216EFB00112F41 /* UserDefaultsTestKey.swift */; };
9E2AC0FF27D8EC120042AA47 /* MnemonicSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9E2AC0FE27D8EC120042AA47 /* MnemonicSwift */; };
9E2AC10127D8EF0B0042AA47 /* WrappedMnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2AC10027D8EF0B0042AA47 /* WrappedMnemonic.swift */; };
9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */; };
9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */; };
9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */; };
@ -111,22 +122,15 @@
9E39112E283F91600073DD9A /* ZatoshiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E39112D283F91600073DD9A /* ZatoshiTests.swift */; };
9E391132284644580073DD9A /* AppInitializationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E391131284644580073DD9A /* AppInitializationTests.swift */; };
9E3911392848AD500073DD9A /* HomeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911382848AD500073DD9A /* HomeTests.swift */; };
9E39113B2848D5180073DD9A /* WrappedNumberFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E39113A2848D5180073DD9A /* WrappedNumberFormatter.swift */; };
9E39113F2848EC360073DD9A /* WrappedRecoveryPhraseRandomizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E39113E2848EC350073DD9A /* WrappedRecoveryPhraseRandomizer.swift */; };
9E3911482848EEB90073DD9A /* RecoveryPhraseRandomizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */; };
9E3911492848EEB90073DD9A /* URIParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911432848EEB90073DD9A /* URIParser.swift */; };
9E39114A2848EEB90073DD9A /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911442848EEB90073DD9A /* UserPreferencesStorage.swift */; };
9E39114B2848EEB90073DD9A /* ZCashSDKEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911452848EEB90073DD9A /* ZCashSDKEnvironment.swift */; };
9E39114C2848EEB90073DD9A /* DatabaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911462848EEB90073DD9A /* DatabaseFiles.swift */; };
9E39114D2848EEB90073DD9A /* WalletStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911472848EEB90073DD9A /* WalletStorage.swift */; };
9E39115E284E3E350073DD9A /* secantUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A2526B364180058B01E /* secantUITests.swift */; };
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */; };
9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; };
9E5BF63F2819542C00BA3F17 /* WalletEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */; };
9E5BF641281FD7B600BA3F17 /* TransactionFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */; };
9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF643281FEC9900BA3F17 /* SendTests.swift */; };
9E5BF6462821028C00BA3F17 /* WrappedUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */; };
9E5BF648282277BE00BA3F17 /* WrappedNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */; };
9E5BF648282277BE00BA3F17 /* NotificationCenterInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */; };
9E5BF64F2823E94900BA3F17 /* TransactionAddressTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */; };
9E5BF6502823E94900BA3F17 /* TransactionAddressTextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */; };
9E66122A287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E661229287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift */; };
@ -140,11 +144,6 @@
9E6713F8289BC58C00A6796F /* BalanceBreakdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6713F5289BC58C00A6796F /* BalanceBreakdownView.swift */; };
9E6713FA289BE0E100A6796F /* ClearBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6713F9289BE0E100A6796F /* ClearBackgroundView.swift */; };
9E69A24D27FB002800A55317 /* WelcomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E69A24C27FB002800A55317 /* WelcomeStore.swift */; };
9E6EF2CB291287BB00CA007B /* FeedbackGeneratorKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6EF2CA291287BB00CA007B /* FeedbackGeneratorKey.swift */; };
9E6EF2CD2913B06300CA007B /* NumberFormatterKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6EF2CC2913B06300CA007B /* NumberFormatterKey.swift */; };
9E6EF2CF2913B11A00CA007B /* SDKSynchronizerKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6EF2CE2913B11A00CA007B /* SDKSynchronizerKey.swift */; };
9E6EF2D12913B75400CA007B /* MnemonicKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6EF2D02913B75400CA007B /* MnemonicKey.swift */; };
9E6EF2D32913B79A00CA007B /* WalletStorageKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6EF2D22913B79A00CA007B /* WalletStorageKey.swift */; };
9E7225F12889539300DF7F17 /* SettingsSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7225F02889539300DF7F17 /* SettingsSnapshotTests.swift */; };
9E7225F3288AB6DD00DF7F17 /* MultipleLineTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7225F2288AB6DD00DF7F17 /* MultipleLineTextField.swift */; };
9E7225F6288AC71A00DF7F17 /* MultiLineTextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7225F5288AC71A00DF7F17 /* MultiLineTextFieldStore.swift */; };
@ -159,8 +158,6 @@
9E7FE0D3282D274E00C374E8 /* Date+Readable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */; };
9E7FE0D5282D281800C374E8 /* Array+Chunked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */; };
9E7FE0D7282D286500C374E8 /* RecoveryPhrase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D6282D286500C374E8 /* RecoveryPhrase.swift */; };
9E7FE0D9282D289B00C374E8 /* WrappedFeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D8282D289B00C374E8 /* WrappedFeedbackGenerator.swift */; };
9E7FE0DB282D28F100C374E8 /* WrappedPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0DA282D28F100C374E8 /* WrappedPasteboard.swift */; };
9E7FE0DD282D298900C374E8 /* ValidationWord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0DC282D298900C374E8 /* ValidationWord.swift */; };
9E7FE0DF282D2DD600C374E8 /* ZcashBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0DE282D2DD600C374E8 /* ZcashBadge.swift */; };
9E7FE0E6282E7B1100C374E8 /* StoredWallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0E5282E7B1100C374E8 /* StoredWallet.swift */; };
@ -183,13 +180,41 @@
9EAB46782860A1D2002904A0 /* WalletEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAB46772860A1D2002904A0 /* WalletEvent.swift */; };
9EAB467A2861EA6A002904A0 /* TransactionRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAB46792861EA6A002904A0 /* TransactionRowView.swift */; };
9EAFEB822805793200199FC9 /* AppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppTests.swift */; };
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; };
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */; };
9EAFEB882806E5AE00199FC9 /* WrappedSDKSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */; };
9EAFEB84280597B700199FC9 /* SecItemInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* SecItemInterface.swift */; };
9EAFEB862805A23100199FC9 /* SecItemClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* SecItemClientTests.swift */; };
9EAFEB882806E5AE00199FC9 /* SDKSynchronizerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB872806E5AE00199FC9 /* SDKSynchronizerInterface.swift */; };
9EAFEB8F2808183D00199FC9 /* SandboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB8D2808183D00199FC9 /* SandboxView.swift */; };
9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */; };
9EAFEB9128081E9400199FC9 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; };
9EAFEB9228081E9400199FC9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874EF273C4DE200F0E875 /* HomeView.swift */; };
9EB8638C2922CD4A003D0F8B /* FeedbackGeneratorTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863892922CC4D003D0F8B /* FeedbackGeneratorTestKey.swift */; };
9EB8638D2922CD4A003D0F8B /* FeedbackGeneratorLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB8638B2922CC4D003D0F8B /* FeedbackGeneratorLiveKey.swift */; };
9EB8638E2922CD4A003D0F8B /* FeedbackGeneratorInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB8638A2922CC4D003D0F8B /* FeedbackGeneratorInterface.swift */; };
9EB863932922D036003D0F8B /* NumberFormatterInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863902922D035003D0F8B /* NumberFormatterInterface.swift */; };
9EB863942922D036003D0F8B /* NumberFormatterLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863912922D035003D0F8B /* NumberFormatterLiveKey.swift */; };
9EB863952922D036003D0F8B /* NumberFormatterTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863922922D036003D0F8B /* NumberFormatterTestKey.swift */; };
9EB8639A2923935B003D0F8B /* WalletStorageTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863972923935B003D0F8B /* WalletStorageTestKey.swift */; };
9EB8639B2923935B003D0F8B /* WalletStorageInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863982923935B003D0F8B /* WalletStorageInterface.swift */; };
9EB8639C2923935B003D0F8B /* WalletStorageLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863992923935B003D0F8B /* WalletStorageLiveKey.swift */; };
9EB8639D29239405003D0F8B /* WalletStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911472848EEB90073DD9A /* WalletStorage.swift */; };
9EB863A1292398A8003D0F8B /* URIParserInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB8639F292398A8003D0F8B /* URIParserInterface.swift */; };
9EB863A2292398A8003D0F8B /* URIParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863A0292398A8003D0F8B /* URIParser.swift */; };
9EB863A729239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863A429239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift */; };
9EB863A829239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863A529239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift */; };
9EB863A929239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863A629239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift */; };
9EB863AA29239EB2003D0F8B /* RecoveryPhraseRandomizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */; };
9EB863B92923C6D7003D0F8B /* FileManagerLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */; };
9EB863BB2923C6F8003D0F8B /* NotificationCenterLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863BA2923C6F8003D0F8B /* NotificationCenterLive.swift */; };
9EB863BD2923C704003D0F8B /* NotificationCenterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863BC2923C704003D0F8B /* NotificationCenterTest.swift */; };
9EB863BF2923C72C003D0F8B /* SecItemLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863BE2923C72C003D0F8B /* SecItemLive.swift */; };
9EB863C12923C779003D0F8B /* URIParserLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C02923C779003D0F8B /* URIParserLive.swift */; };
9EB863C32923C807003D0F8B /* URIParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C22923C807003D0F8B /* URIParserTest.swift */; };
9EB863C52923C8AF003D0F8B /* FileManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */; };
9EB863C72923C93B003D0F8B /* UserPreferencesStorageLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C62923C93B003D0F8B /* UserPreferencesStorageLive.swift */; };
9EB863C92923C953003D0F8B /* UserPreferencesStorageMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */; };
9EB863CB2923CA20003D0F8B /* SDKSynchronizerLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */; };
9EB863CD2923CA28003D0F8B /* SDKSynchronizerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863CC2923CA28003D0F8B /* SDKSynchronizerTest.swift */; };
9EB863D02923D3FC003D0F8B /* SDKSynchronizerMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */; };
9EBDF947291D75B2000A1A05 /* DiskSpaceCheckerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF946291D75B2000A1A05 /* DiskSpaceCheckerInterface.swift */; };
9EBDF949291D75BF000A1A05 /* DiskSpaceCheckerLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF948291D75BF000A1A05 /* DiskSpaceCheckerLiveKey.swift */; };
9EBDF94B291D75C7000A1A05 /* DiskSpaceCheckerTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF94A291D75C7000A1A05 /* DiskSpaceCheckerTestKey.swift */; };
@ -197,7 +222,6 @@
9EBDF953291E5E86000A1A05 /* DatabaseFilesTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF94F291E5E86000A1A05 /* DatabaseFilesTestKey.swift */; };
9EBDF955291E5E86000A1A05 /* DatabaseFilesInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF951291E5E86000A1A05 /* DatabaseFilesInterface.swift */; };
9EBDF956291E5E86000A1A05 /* DatabaseFilesLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF952291E5E86000A1A05 /* DatabaseFilesLiveKey.swift */; };
9EBDF958291E6418000A1A05 /* DatabaseFilesMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF957291E6418000A1A05 /* DatabaseFilesMocks.swift */; };
9EBDF95F291E657B000A1A05 /* DeeplinkLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF95B291E657B000A1A05 /* DeeplinkLiveKey.swift */; };
9EBDF960291E657B000A1A05 /* DeeplinkTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF95C291E657B000A1A05 /* DeeplinkTestKey.swift */; };
9EBDF961291E657B000A1A05 /* DeeplinkInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF95D291E657B000A1A05 /* DeeplinkInterface.swift */; };
@ -222,12 +246,9 @@
9EDDEAA22829610D00B4100C /* CurrencySelectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDDEA9F2829610D00B4100C /* CurrencySelectionTests.swift */; };
9EDDEAA32829610D00B4100C /* TransactionAmountInputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDDEAA02829610D00B4100C /* TransactionAmountInputTests.swift */; };
9EDDEAA42829610D00B4100C /* TransactionAddressInputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDDEAA12829610D00B4100C /* TransactionAddressInputTests.swift */; };
9EF1082B29114B93003D8097 /* PasteboardKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF1082A29114B93003D8097 /* PasteboardKey.swift */; };
9EF1082D29114BCD003D8097 /* NewRecoveryPhraseKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF1082C29114BCD003D8097 /* NewRecoveryPhraseKey.swift */; };
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */; };
9EF8135D27ECC25E0075AF48 /* UserPreferencesStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */; };
9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135F27F043CC0075AF48 /* AppDelegate.swift */; };
9EF8139127F191BF0075AF48 /* WrappedWalletStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139027F191BF0075AF48 /* WrappedWalletStorage.swift */; };
9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139B27F47AED0075AF48 /* InitializationState.swift */; };
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; };
@ -341,7 +362,7 @@
2EDA079F27EDE18C00D6F09B /* TCATextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCATextField.swift; sourceTree = "<group>"; };
2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldFooter.swift; sourceTree = "<group>"; };
2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugFrame.swift; sourceTree = "<group>"; };
34429C6D28E703CD00F2B929 /* TransactionSendingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionSendingTests.swift; sourceTree = "<group>"; };
34429C6D28E703CD00F2B929 /* TransactionSendingSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionSendingSnapshotTests.swift; sourceTree = "<group>"; };
3448CB3128E47666006ADEDB /* NotEnoughFreeSpaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotEnoughFreeSpaceView.swift; sourceTree = "<group>"; };
3448CB3628E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotEnoughFeeSpaceSnapshots.swift; sourceTree = "<group>"; };
346715A428E2027D0035F7C4 /* CheckCircleStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckCircleStore.swift; sourceTree = "<group>"; };
@ -366,11 +387,22 @@
66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = "<group>"; };
66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = "<group>"; };
66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = "<group>"; };
9E01F8232833C0D8000EFC57 /* WrappedURIParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedURIParser.swift; sourceTree = "<group>"; };
9E01F8272833CDA0000EFC57 /* ScanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanTests.swift; sourceTree = "<group>"; };
9E02B56927FED43E005B809B /* WrappedFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedFileManager.swift; sourceTree = "<group>"; };
9E02B56927FED43E005B809B /* FileManagerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerInterface.swift; sourceTree = "<group>"; };
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFilesTests.swift; sourceTree = "<group>"; };
9E2AC10027D8EF0B0042AA47 /* WrappedMnemonic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedMnemonic.swift; sourceTree = "<group>"; };
9E153A5B2920CD5100112F41 /* MnemonicMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicMocks.swift; sourceTree = "<group>"; };
9E153A5C2920CD5100112F41 /* MnemonicLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicLiveKey.swift; sourceTree = "<group>"; };
9E153A5D2920CD5100112F41 /* MnemonicTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicTestKey.swift; sourceTree = "<group>"; };
9E153A5E2920CD5100112F41 /* MnemonicInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicInterface.swift; sourceTree = "<group>"; };
9E153A6429210B3B00112F41 /* PasteboardLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasteboardLiveKey.swift; sourceTree = "<group>"; };
9E153A6529210B3B00112F41 /* PasteboardInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasteboardInterface.swift; sourceTree = "<group>"; };
9E153A6629210B3B00112F41 /* PasteboardTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasteboardTestKey.swift; sourceTree = "<group>"; };
9E153A6B292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZcashSDKEnvironmentLiveKey.swift; sourceTree = "<group>"; };
9E153A6C292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZcashSDKEnvironmentTestKey.swift; sourceTree = "<group>"; };
9E153A6D292167FF00112F41 /* ZcashSDKEnvironmentInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZcashSDKEnvironmentInterface.swift; sourceTree = "<group>"; };
9E153A7229216EFB00112F41 /* UserDefaultsLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsLiveKey.swift; sourceTree = "<group>"; };
9E153A7329216EFB00112F41 /* UserDefaultsInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsInterface.swift; sourceTree = "<group>"; };
9E153A7429216EFB00112F41 /* UserDefaultsTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsTestKey.swift; sourceTree = "<group>"; };
9E2DF99827CF704D00649636 /* ImportWalletStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletStore.swift; sourceTree = "<group>"; };
9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportSeedEditor.swift; sourceTree = "<group>"; };
9E2DF99B27CF704D00649636 /* ImportWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = "<group>"; };
@ -382,12 +414,8 @@
9E39112D283F91600073DD9A /* ZatoshiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZatoshiTests.swift; sourceTree = "<group>"; };
9E391131284644580073DD9A /* AppInitializationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInitializationTests.swift; sourceTree = "<group>"; };
9E3911382848AD500073DD9A /* HomeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTests.swift; sourceTree = "<group>"; };
9E39113A2848D5180073DD9A /* WrappedNumberFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedNumberFormatter.swift; sourceTree = "<group>"; };
9E39113E2848EC350073DD9A /* WrappedRecoveryPhraseRandomizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WrappedRecoveryPhraseRandomizer.swift; sourceTree = "<group>"; };
9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizer.swift; sourceTree = "<group>"; };
9E3911432848EEB90073DD9A /* URIParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URIParser.swift; sourceTree = "<group>"; };
9E3911442848EEB90073DD9A /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = "<group>"; };
9E3911452848EEB90073DD9A /* ZCashSDKEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZCashSDKEnvironment.swift; sourceTree = "<group>"; };
9E3911462848EEB90073DD9A /* DatabaseFiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = "<group>"; };
9E3911472848EEB90073DD9A /* WalletStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorage.swift; sourceTree = "<group>"; };
9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphicDesignModifier.swift; sourceTree = "<group>"; };
@ -396,8 +424,7 @@
9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEventsTests.swift; sourceTree = "<group>"; };
9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionFailedView.swift; sourceTree = "<group>"; };
9E5BF643281FEC9900BA3F17 /* SendTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendTests.swift; sourceTree = "<group>"; };
9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedUserDefaults.swift; sourceTree = "<group>"; };
9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedNotificationCenter.swift; sourceTree = "<group>"; };
9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterInterface.swift; sourceTree = "<group>"; };
9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextField.swift; sourceTree = "<group>"; };
9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextFieldStore.swift; sourceTree = "<group>"; };
9E661229287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCircularProgressSnapshotTests.swift; sourceTree = "<group>"; };
@ -410,11 +437,6 @@
9E6713F6289BC58C00A6796F /* BalanceBreakdownStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceBreakdownStore.swift; sourceTree = "<group>"; };
9E6713F9289BE0E100A6796F /* ClearBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearBackgroundView.swift; sourceTree = "<group>"; };
9E69A24C27FB002800A55317 /* WelcomeStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStore.swift; sourceTree = "<group>"; };
9E6EF2CA291287BB00CA007B /* FeedbackGeneratorKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGeneratorKey.swift; sourceTree = "<group>"; };
9E6EF2CC2913B06300CA007B /* NumberFormatterKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberFormatterKey.swift; sourceTree = "<group>"; };
9E6EF2CE2913B11A00CA007B /* SDKSynchronizerKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerKey.swift; sourceTree = "<group>"; };
9E6EF2D02913B75400CA007B /* MnemonicKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicKey.swift; sourceTree = "<group>"; };
9E6EF2D22913B79A00CA007B /* WalletStorageKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletStorageKey.swift; sourceTree = "<group>"; };
9E7225F02889539300DF7F17 /* SettingsSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSnapshotTests.swift; sourceTree = "<group>"; };
9E7225F2288AB6DD00DF7F17 /* MultipleLineTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleLineTextField.swift; sourceTree = "<group>"; };
9E7225F5288AC71A00DF7F17 /* MultiLineTextFieldStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiLineTextFieldStore.swift; sourceTree = "<group>"; };
@ -428,8 +450,6 @@
9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Readable.swift"; sourceTree = "<group>"; };
9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Chunked.swift"; sourceTree = "<group>"; };
9E7FE0D6282D286500C374E8 /* RecoveryPhrase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhrase.swift; sourceTree = "<group>"; };
9E7FE0D8282D289B00C374E8 /* WrappedFeedbackGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedFeedbackGenerator.swift; sourceTree = "<group>"; };
9E7FE0DA282D28F100C374E8 /* WrappedPasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedPasteboard.swift; sourceTree = "<group>"; };
9E7FE0DC282D298900C374E8 /* ValidationWord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidationWord.swift; sourceTree = "<group>"; };
9E7FE0DE282D2DD600C374E8 /* ZcashBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashBadge.swift; sourceTree = "<group>"; };
9E7FE0E5282E7B1100C374E8 /* StoredWallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredWallet.swift; sourceTree = "<group>"; };
@ -449,11 +469,37 @@
9EAB46772860A1D2002904A0 /* WalletEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEvent.swift; sourceTree = "<group>"; };
9EAB46792861EA6A002904A0 /* TransactionRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionRowView.swift; sourceTree = "<group>"; };
9EAFEB812805793200199FC9 /* AppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTests.swift; sourceTree = "<group>"; };
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = "<group>"; };
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = "<group>"; };
9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSDKSynchronizer.swift; sourceTree = "<group>"; };
9EAFEB83280597B700199FC9 /* SecItemInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecItemInterface.swift; sourceTree = "<group>"; };
9EAFEB852805A23100199FC9 /* SecItemClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecItemClientTests.swift; sourceTree = "<group>"; };
9EAFEB872806E5AE00199FC9 /* SDKSynchronizerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerInterface.swift; sourceTree = "<group>"; };
9EAFEB8D2808183D00199FC9 /* SandboxView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxView.swift; sourceTree = "<group>"; };
9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxStore.swift; sourceTree = "<group>"; };
9EB863892922CC4D003D0F8B /* FeedbackGeneratorTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGeneratorTestKey.swift; sourceTree = "<group>"; };
9EB8638A2922CC4D003D0F8B /* FeedbackGeneratorInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGeneratorInterface.swift; sourceTree = "<group>"; };
9EB8638B2922CC4D003D0F8B /* FeedbackGeneratorLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGeneratorLiveKey.swift; sourceTree = "<group>"; };
9EB863902922D035003D0F8B /* NumberFormatterInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberFormatterInterface.swift; sourceTree = "<group>"; };
9EB863912922D035003D0F8B /* NumberFormatterLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberFormatterLiveKey.swift; sourceTree = "<group>"; };
9EB863922922D036003D0F8B /* NumberFormatterTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberFormatterTestKey.swift; sourceTree = "<group>"; };
9EB863972923935B003D0F8B /* WalletStorageTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTestKey.swift; sourceTree = "<group>"; };
9EB863982923935B003D0F8B /* WalletStorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageInterface.swift; sourceTree = "<group>"; };
9EB863992923935B003D0F8B /* WalletStorageLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageLiveKey.swift; sourceTree = "<group>"; };
9EB8639F292398A8003D0F8B /* URIParserInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URIParserInterface.swift; sourceTree = "<group>"; };
9EB863A0292398A8003D0F8B /* URIParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URIParser.swift; sourceTree = "<group>"; };
9EB863A429239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerTestKey.swift; sourceTree = "<group>"; };
9EB863A529239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerLiveKey.swift; sourceTree = "<group>"; };
9EB863A629239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerInterface.swift; sourceTree = "<group>"; };
9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerLive.swift; sourceTree = "<group>"; };
9EB863BA2923C6F8003D0F8B /* NotificationCenterLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterLive.swift; sourceTree = "<group>"; };
9EB863BC2923C704003D0F8B /* NotificationCenterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterTest.swift; sourceTree = "<group>"; };
9EB863BE2923C72C003D0F8B /* SecItemLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecItemLive.swift; sourceTree = "<group>"; };
9EB863C02923C779003D0F8B /* URIParserLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIParserLive.swift; sourceTree = "<group>"; };
9EB863C22923C807003D0F8B /* URIParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIParserTest.swift; sourceTree = "<group>"; };
9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerTest.swift; sourceTree = "<group>"; };
9EB863C62923C93B003D0F8B /* UserPreferencesStorageLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageLive.swift; sourceTree = "<group>"; };
9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageMocks.swift; sourceTree = "<group>"; };
9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerLive.swift; sourceTree = "<group>"; };
9EB863CC2923CA28003D0F8B /* SDKSynchronizerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerTest.swift; sourceTree = "<group>"; };
9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerMocks.swift; sourceTree = "<group>"; };
9EBDF946291D75B2000A1A05 /* DiskSpaceCheckerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerInterface.swift; sourceTree = "<group>"; };
9EBDF948291D75BF000A1A05 /* DiskSpaceCheckerLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerLiveKey.swift; sourceTree = "<group>"; };
9EBDF94A291D75C7000A1A05 /* DiskSpaceCheckerTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerTestKey.swift; sourceTree = "<group>"; };
@ -461,7 +507,6 @@
9EBDF94F291E5E86000A1A05 /* DatabaseFilesTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFilesTestKey.swift; sourceTree = "<group>"; };
9EBDF951291E5E86000A1A05 /* DatabaseFilesInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFilesInterface.swift; sourceTree = "<group>"; };
9EBDF952291E5E86000A1A05 /* DatabaseFilesLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFilesLiveKey.swift; sourceTree = "<group>"; };
9EBDF957291E6418000A1A05 /* DatabaseFilesMocks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFilesMocks.swift; sourceTree = "<group>"; };
9EBDF95B291E657B000A1A05 /* DeeplinkLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkLiveKey.swift; sourceTree = "<group>"; };
9EBDF95C291E657B000A1A05 /* DeeplinkTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkTestKey.swift; sourceTree = "<group>"; };
9EBDF95D291E657B000A1A05 /* DeeplinkInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkInterface.swift; sourceTree = "<group>"; };
@ -486,12 +531,9 @@
9EDDEA9F2829610D00B4100C /* CurrencySelectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySelectionTests.swift; sourceTree = "<group>"; };
9EDDEAA02829610D00B4100C /* TransactionAmountInputTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAmountInputTests.swift; sourceTree = "<group>"; };
9EDDEAA12829610D00B4100C /* TransactionAddressInputTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressInputTests.swift; sourceTree = "<group>"; };
9EF1082A29114B93003D8097 /* PasteboardKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardKey.swift; sourceTree = "<group>"; };
9EF1082C29114BCD003D8097 /* NewRecoveryPhraseKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewRecoveryPhraseKey.swift; sourceTree = "<group>"; };
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = "<group>"; };
9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageTests.swift; sourceTree = "<group>"; };
9EF8135F27F043CC0075AF48 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
9EF8139027F191BF0075AF48 /* WrappedWalletStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedWalletStorage.swift; sourceTree = "<group>"; };
9EF8139B27F47AED0075AF48 /* InitializationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitializationState.swift; sourceTree = "<group>"; };
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = "<group>"; };
@ -616,7 +658,6 @@
isa = PBXGroup;
children = (
0DA13CA326C1960A00E3B610 /* Models */,
9E02B56827FED42D005B809B /* Wrappers */,
9E7FE0BB282D1DC200C374E8 /* Utils */,
9E7FE0BD282D1DE100C374E8 /* Dependencies */,
6654C73B2715A3F000901167 /* Features */,
@ -798,7 +839,7 @@
isa = PBXGroup;
children = (
346715A728E20FE40035F7C4 /* TransactionConfirmationSnapshotTests.swift */,
34429C6D28E703CD00F2B929 /* TransactionSendingTests.swift */,
34429C6D28E703CD00F2B929 /* TransactionSendingSnapshotTests.swift */,
);
path = SendSnapshotTests;
sourceTree = "<group>";
@ -912,23 +953,45 @@
path = ScanTests;
sourceTree = "<group>";
};
9E02B56827FED42D005B809B /* Wrappers */ = {
9E153A5A2920CCE700112F41 /* Mnemonic */ = {
isa = PBXGroup;
children = (
9E7FE0D8282D289B00C374E8 /* WrappedFeedbackGenerator.swift */,
9E02B56927FED43E005B809B /* WrappedFileManager.swift */,
9E2AC10027D8EF0B0042AA47 /* WrappedMnemonic.swift */,
9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */,
9E39113A2848D5180073DD9A /* WrappedNumberFormatter.swift */,
9E7FE0DA282D28F100C374E8 /* WrappedPasteboard.swift */,
9E39113E2848EC350073DD9A /* WrappedRecoveryPhraseRandomizer.swift */,
9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */,
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */,
9E01F8232833C0D8000EFC57 /* WrappedURIParser.swift */,
9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */,
9EF8139027F191BF0075AF48 /* WrappedWalletStorage.swift */,
9E153A5E2920CD5100112F41 /* MnemonicInterface.swift */,
9E153A5C2920CD5100112F41 /* MnemonicLiveKey.swift */,
9E153A5D2920CD5100112F41 /* MnemonicTestKey.swift */,
9E153A5B2920CD5100112F41 /* MnemonicMocks.swift */,
);
path = Wrappers;
path = Mnemonic;
sourceTree = "<group>";
};
9E153A6329210AF800112F41 /* Pasteboard */ = {
isa = PBXGroup;
children = (
9E153A6529210B3B00112F41 /* PasteboardInterface.swift */,
9E153A6429210B3B00112F41 /* PasteboardLiveKey.swift */,
9E153A6629210B3B00112F41 /* PasteboardTestKey.swift */,
);
path = Pasteboard;
sourceTree = "<group>";
};
9E153A6A292167BF00112F41 /* ZcashSDKEnvironment */ = {
isa = PBXGroup;
children = (
9E153A6D292167FF00112F41 /* ZcashSDKEnvironmentInterface.swift */,
9E153A6B292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift */,
9E153A6C292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift */,
);
path = ZcashSDKEnvironment;
sourceTree = "<group>";
};
9E153A7129216EBD00112F41 /* UserDefaults */ = {
isa = PBXGroup;
children = (
9E153A7329216EFB00112F41 /* UserDefaultsInterface.swift */,
9E153A7229216EFB00112F41 /* UserDefaultsLiveKey.swift */,
9E153A7429216EFB00112F41 /* UserDefaultsTestKey.swift */,
);
path = UserDefaults;
sourceTree = "<group>";
};
9E2DF99727CF704D00649636 /* ImportWallet */ = {
@ -1167,19 +1230,21 @@
9EBDF959291E654F000A1A05 /* Deeplink */,
9EBDF971291F79C9000A1A05 /* DerivationTool */,
9EBDF945291D759B000A1A05 /* DiskSpaceChecker */,
9EB863882922CC0E003D0F8B /* FeedbackGenerator */,
9EB863B52923C4ED003D0F8B /* FileManager */,
9EBDF981291F91B1000A1A05 /* LocalAuthentication */,
9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */,
9E3911432848EEB90073DD9A /* URIParser.swift */,
9E3911442848EEB90073DD9A /* UserPreferencesStorage.swift */,
9E3911472848EEB90073DD9A /* WalletStorage.swift */,
9E3911452848EEB90073DD9A /* ZCashSDKEnvironment.swift */,
9E6EF2D02913B75400CA007B /* MnemonicKey.swift */,
9E6EF2D22913B79A00CA007B /* WalletStorageKey.swift */,
9EF1082A29114B93003D8097 /* PasteboardKey.swift */,
9EF1082C29114BCD003D8097 /* NewRecoveryPhraseKey.swift */,
9E6EF2CA291287BB00CA007B /* FeedbackGeneratorKey.swift */,
9E6EF2CC2913B06300CA007B /* NumberFormatterKey.swift */,
9E6EF2CE2913B11A00CA007B /* SDKSynchronizerKey.swift */,
9E153A5A2920CCE700112F41 /* Mnemonic */,
9EB863B42923C490003D0F8B /* NotificationCenter */,
9EB8638F2922D000003D0F8B /* NumberFormatter */,
9E153A6329210AF800112F41 /* Pasteboard */,
9EB863A329239D95003D0F8B /* RecoveryPhraseRandomizer */,
9EB863B62923C539003D0F8B /* SDKSynchronizer */,
9EB863B32923C465003D0F8B /* SecItem */,
9EB8639E29239891003D0F8B /* URIParser */,
9E153A7129216EBD00112F41 /* UserDefaults */,
9EB863B72923C55A003D0F8B /* UserPreferencesStorage */,
9EB86396292392F6003D0F8B /* WalletStorage */,
9E153A6A292167BF00112F41 /* ZcashSDKEnvironment */,
);
path = Dependencies;
sourceTree = "<group>";
@ -1360,6 +1425,109 @@
path = Sandbox;
sourceTree = "<group>";
};
9EB863882922CC0E003D0F8B /* FeedbackGenerator */ = {
isa = PBXGroup;
children = (
9EB8638A2922CC4D003D0F8B /* FeedbackGeneratorInterface.swift */,
9EB8638B2922CC4D003D0F8B /* FeedbackGeneratorLiveKey.swift */,
9EB863892922CC4D003D0F8B /* FeedbackGeneratorTestKey.swift */,
);
path = FeedbackGenerator;
sourceTree = "<group>";
};
9EB8638F2922D000003D0F8B /* NumberFormatter */ = {
isa = PBXGroup;
children = (
9EB863902922D035003D0F8B /* NumberFormatterInterface.swift */,
9EB863912922D035003D0F8B /* NumberFormatterLiveKey.swift */,
9EB863922922D036003D0F8B /* NumberFormatterTestKey.swift */,
);
path = NumberFormatter;
sourceTree = "<group>";
};
9EB86396292392F6003D0F8B /* WalletStorage */ = {
isa = PBXGroup;
children = (
9E3911472848EEB90073DD9A /* WalletStorage.swift */,
9EB863982923935B003D0F8B /* WalletStorageInterface.swift */,
9EB863992923935B003D0F8B /* WalletStorageLiveKey.swift */,
9EB863972923935B003D0F8B /* WalletStorageTestKey.swift */,
);
path = WalletStorage;
sourceTree = "<group>";
};
9EB8639E29239891003D0F8B /* URIParser */ = {
isa = PBXGroup;
children = (
9EB863A0292398A8003D0F8B /* URIParser.swift */,
9EB8639F292398A8003D0F8B /* URIParserInterface.swift */,
9EB863C02923C779003D0F8B /* URIParserLive.swift */,
9EB863C22923C807003D0F8B /* URIParserTest.swift */,
);
path = URIParser;
sourceTree = "<group>";
};
9EB863A329239D95003D0F8B /* RecoveryPhraseRandomizer */ = {
isa = PBXGroup;
children = (
9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */,
9EB863A629239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift */,
9EB863A529239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift */,
9EB863A429239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift */,
);
path = RecoveryPhraseRandomizer;
sourceTree = "<group>";
};
9EB863B32923C465003D0F8B /* SecItem */ = {
isa = PBXGroup;
children = (
9EAFEB83280597B700199FC9 /* SecItemInterface.swift */,
9EB863BE2923C72C003D0F8B /* SecItemLive.swift */,
);
path = SecItem;
sourceTree = "<group>";
};
9EB863B42923C490003D0F8B /* NotificationCenter */ = {
isa = PBXGroup;
children = (
9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */,
9EB863BA2923C6F8003D0F8B /* NotificationCenterLive.swift */,
9EB863BC2923C704003D0F8B /* NotificationCenterTest.swift */,
);
path = NotificationCenter;
sourceTree = "<group>";
};
9EB863B52923C4ED003D0F8B /* FileManager */ = {
isa = PBXGroup;
children = (
9E02B56927FED43E005B809B /* FileManagerInterface.swift */,
9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */,
9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */,
);
path = FileManager;
sourceTree = "<group>";
};
9EB863B62923C539003D0F8B /* SDKSynchronizer */ = {
isa = PBXGroup;
children = (
9EAFEB872806E5AE00199FC9 /* SDKSynchronizerInterface.swift */,
9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */,
9EB863CC2923CA28003D0F8B /* SDKSynchronizerTest.swift */,
9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */,
);
path = SDKSynchronizer;
sourceTree = "<group>";
};
9EB863B72923C55A003D0F8B /* UserPreferencesStorage */ = {
isa = PBXGroup;
children = (
9E3911442848EEB90073DD9A /* UserPreferencesStorage.swift */,
9EB863C62923C93B003D0F8B /* UserPreferencesStorageLive.swift */,
9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */,
);
path = UserPreferencesStorage;
sourceTree = "<group>";
};
9EBDF945291D759B000A1A05 /* DiskSpaceChecker */ = {
isa = PBXGroup;
children = (
@ -1379,7 +1547,6 @@
9EBDF951291E5E86000A1A05 /* DatabaseFilesInterface.swift */,
9EBDF952291E5E86000A1A05 /* DatabaseFilesLiveKey.swift */,
9EBDF94F291E5E86000A1A05 /* DatabaseFilesTestKey.swift */,
9EBDF957291E6418000A1A05 /* DatabaseFilesMocks.swift */,
);
path = DatabaseFiles;
sourceTree = "<group>";
@ -1451,7 +1618,7 @@
isa = PBXGroup;
children = (
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */,
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */,
9EAFEB852805A23100199FC9 /* SecItemClientTests.swift */,
9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */,
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */,
9E39112D283F91600073DD9A /* ZatoshiTests.swift */,
@ -1785,14 +1952,17 @@
9EBDF975291F79F9000A1A05 /* DerivationToolInterface.swift in Sources */,
660558F8270C862F009D6954 /* XCAssets+Generated.swift in Sources */,
9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */,
9EB863A929239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift in Sources */,
0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */,
9E39114A2848EEB90073DD9A /* UserPreferencesStorage.swift in Sources */,
9E153A612920CE2700112F41 /* MnemonicMocks.swift in Sources */,
34DA414728E4385800F8CC61 /* TransactionSendingView.swift in Sources */,
F96B41E9273B501F0021B49A /* WalletEventsFlowView.swift in Sources */,
9EBDF96E291ECED4000A1A05 /* CaptureDeviceLiveKey.swift in Sources */,
9EBDF968291ECDA2000A1A05 /* AudioServicesInterface.swift in Sources */,
9EB863BD2923C704003D0F8B /* NotificationCenterTest.swift in Sources */,
9EB863A829239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift in Sources */,
2EDA07A027EDE18C00D6F09B /* TCATextField.swift in Sources */,
9E7FE0DB282D28F100C374E8 /* WrappedPasteboard.swift in Sources */,
9EBDF961291E657B000A1A05 /* DeeplinkInterface.swift in Sources */,
9EBDF977291F79F9000A1A05 /* DerivationToolTestKey.swift in Sources */,
2EB7758727FC67FD00269373 /* TransactionAmountTextFieldStore.swift in Sources */,
@ -1801,29 +1971,30 @@
9EBDF960291E657B000A1A05 /* DeeplinkTestKey.swift in Sources */,
34E0AF1128DEE5220034CF37 /* Wedge.swift in Sources */,
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */,
9EB863942922D036003D0F8B /* NumberFormatterLiveKey.swift in Sources */,
9EBDF947291D75B2000A1A05 /* DiskSpaceCheckerInterface.swift in Sources */,
9E02B56A27FED43E005B809B /* WrappedFileManager.swift in Sources */,
9E02B56A27FED43E005B809B /* FileManagerInterface.swift in Sources */,
663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */,
9E7CB6202874143800A02233 /* AddressDetailsStore.swift in Sources */,
9E6EF2D12913B75400CA007B /* MnemonicKey.swift in Sources */,
0DC487C32772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift in Sources */,
2EB1C5E827D77F6100BC43D7 /* TCATextFieldStore.swift in Sources */,
9E5BF648282277BE00BA3F17 /* WrappedNotificationCenter.swift in Sources */,
9EF1082D29114BCD003D8097 /* NewRecoveryPhraseKey.swift in Sources */,
9E5BF648282277BE00BA3F17 /* NotificationCenterInterface.swift in Sources */,
0D8A43C4272AEEDE005A6414 /* SecantTextStyles.swift in Sources */,
9E5BF641281FD7B600BA3F17 /* TransactionFailedView.swift in Sources */,
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */,
0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */,
9EAB467A2861EA6A002904A0 /* TransactionRowView.swift in Sources */,
9E6EF2CB291287BB00CA007B /* FeedbackGeneratorKey.swift in Sources */,
9EB8638C2922CD4A003D0F8B /* FeedbackGeneratorTestKey.swift in Sources */,
0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift in Sources */,
9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */,
9EBDF967291ECDA2000A1A05 /* AudioServicesTestKey.swift in Sources */,
0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */,
9EBDF97E291F7EB0000A1A05 /* AppVersionInterface.swift in Sources */,
6654C73E2715A41300901167 /* OnboardingFlowStore.swift in Sources */,
9E7FE0D9282D289B00C374E8 /* WrappedFeedbackGenerator.swift in Sources */,
9EB863CB2923CA20003D0F8B /* SDKSynchronizerLive.swift in Sources */,
9EB863A1292398A8003D0F8B /* URIParserInterface.swift in Sources */,
2E6CF8DD27D78319004DCD7A /* CurrencySelectionStore.swift in Sources */,
9E153A7729216EFB00112F41 /* UserDefaultsTestKey.swift in Sources */,
9EBEF87A27CE369800B4F343 /* RecoveryPhraseValidationFlowView.swift in Sources */,
9E6713F7289BC58C00A6796F /* BalanceBreakdownStore.swift in Sources */,
9E66122C2877188700C75B70 /* SyncStatusSnapshot.swift in Sources */,
@ -1834,28 +2005,34 @@
9EBDF986291F91EF000A1A05 /* LocalAuthenticationLiveKey.swift in Sources */,
9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */,
F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */,
9E39114D2848EEB90073DD9A /* WalletStorage.swift in Sources */,
346D41E428DF0B8600963F36 /* CheckCircle.swift in Sources */,
9EB863AA29239EB2003D0F8B /* RecoveryPhraseRandomizer.swift in Sources */,
9EB863C52923C8AF003D0F8B /* FileManagerTest.swift in Sources */,
9EB863BF2923C72C003D0F8B /* SecItemLive.swift in Sources */,
669FDAEB272C23C2007B9422 /* CircularFrameBadge.swift in Sources */,
9E39113B2848D5180073DD9A /* WrappedNumberFormatter.swift in Sources */,
9EB863B92923C6D7003D0F8B /* FileManagerLive.swift in Sources */,
9EBDF97C291F7EB0000A1A05 /* AppVersionTestKey.swift in Sources */,
2E8719CD27FB0D3B0082C926 /* CurrencySelectionView.swift in Sources */,
9EB863A729239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift in Sources */,
F9971A6C27680E1000A2DB75 /* WalletInfoView.swift in Sources */,
9E5BF6502823E94900BA3F17 /* TransactionAddressTextFieldStore.swift in Sources */,
9EB863932922D036003D0F8B /* NumberFormatterInterface.swift in Sources */,
F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */,
9E7FE0D3282D274E00C374E8 /* Date+Readable.swift in Sources */,
F93673D62742CB840099C6AF /* Previews.swift in Sources */,
9EB8638E2922CD4A003D0F8B /* FeedbackGeneratorInterface.swift in Sources */,
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
9E7FE0F92832824C00C374E8 /* QRCodeScanView.swift in Sources */,
9E3911482848EEB90073DD9A /* RecoveryPhraseRandomizer.swift in Sources */,
9E153A6F292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift in Sources */,
0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */,
9E7225F3288AB6DD00DF7F17 /* MultipleLineTextField.swift in Sources */,
3448CB3228E47666006ADEDB /* NotEnoughFreeSpaceView.swift in Sources */,
9EBDF985291F91EF000A1A05 /* LocalAuthenticationHandlerInterface.swift in Sources */,
9E7FE0EC282E7D9400C374E8 /* TransactionState.swift in Sources */,
9EB863CD2923CA28003D0F8B /* SDKSynchronizerTest.swift in Sources */,
9E2F1C8F280EDE09004E65FE /* Drawer.swift in Sources */,
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
9EAFEB882806E5AE00199FC9 /* WrappedSDKSynchronizer.swift in Sources */,
9EAFEB882806E5AE00199FC9 /* SDKSynchronizerInterface.swift in Sources */,
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,
F9971A4D27680DC400A2DB75 /* AppStore.swift in Sources */,
9EAFEB9228081E9400199FC9 /* HomeView.swift in Sources */,
@ -1863,29 +2040,31 @@
9EAFEB8F2808183D00199FC9 /* SandboxView.swift in Sources */,
0D7CE63427349B5D0020E050 /* View+WhenDraggable.swift in Sources */,
0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */,
9EBDF958291E6418000A1A05 /* DatabaseFilesMocks.swift in Sources */,
9EB863A2292398A8003D0F8B /* URIParser.swift in Sources */,
9EB863C12923C779003D0F8B /* URIParserLive.swift in Sources */,
9EBDF987291F91EF000A1A05 /* LocalAuthenticationTestKey.swift in Sources */,
F9971A5F27680DF600A2DB75 /* ScanView.swift in Sources */,
9E39114C2848EEB90073DD9A /* DatabaseFiles.swift in Sources */,
F9971A4E27680DC400A2DB75 /* AppView.swift in Sources */,
9E3911492848EEB90073DD9A /* URIParser.swift in Sources */,
9EBDF94B291D75C7000A1A05 /* DiskSpaceCheckerTestKey.swift in Sources */,
9E153A7529216EFB00112F41 /* UserDefaultsLiveKey.swift in Sources */,
2EA11F5B27467EF800709571 /* OnboardingFooterView.swift in Sources */,
9EBDF96F291ECED4000A1A05 /* CaptureDeviceTestKey.swift in Sources */,
9EBDF949291D75BF000A1A05 /* DiskSpaceCheckerLiveKey.swift in Sources */,
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */,
9E7225F6288AC71A00DF7F17 /* MultiLineTextFieldStore.swift in Sources */,
2EDA07A427EDE2A900D6F09B /* DebugFrame.swift in Sources */,
9E6EF2CF2913B11A00CA007B /* SDKSynchronizerKey.swift in Sources */,
9E153A602920CE2700112F41 /* MnemonicLiveKey.swift in Sources */,
9E6612332878338C00C75B70 /* LottieAnimation.swift in Sources */,
9EB863C92923C953003D0F8B /* UserPreferencesStorageMocks.swift in Sources */,
0D3D040A2728B3A10032ABC1 /* RecoveryPhraseDisplayStore.swift in Sources */,
9EAB4671285A1C77002904A0 /* Deeplink.swift in Sources */,
9E2AC10127D8EF0B0042AA47 /* WrappedMnemonic.swift in Sources */,
9E7FE0D7282D286500C374E8 /* RecoveryPhrase.swift in Sources */,
9EBDF989291F9428000A1A05 /* LocalAuthenticationMocks.swift in Sources */,
660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */,
F96B41E7273B501F0021B49A /* WalletEventsFlowStore.swift in Sources */,
9E7FE0E6282E7B1100C374E8 /* StoredWallet.swift in Sources */,
9E153A7629216EFB00112F41 /* UserDefaultsInterface.swift in Sources */,
9EAFEB9128081E9400199FC9 /* HomeStore.swift in Sources */,
9EBDF980291F8261000A1A05 /* AppVersionMocks.swift in Sources */,
F9971A5A27680DDE00A2DB75 /* RequestView.swift in Sources */,
@ -1896,18 +2075,21 @@
9EBDF966291ECDA2000A1A05 /* AudioServicesLiveKey.swift in Sources */,
F9C165BF2740403600592F76 /* SendFlowStore.swift in Sources */,
0D4E7A0926B364170058B01E /* SecantApp.swift in Sources */,
9EB863BB2923C6F8003D0F8B /* NotificationCenterLive.swift in Sources */,
9EB8639C2923935B003D0F8B /* WalletStorageLiveKey.swift in Sources */,
9EB8639B2923935B003D0F8B /* WalletStorageInterface.swift in Sources */,
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */,
663FABA0271D876200E495F8 /* PrimaryButton.swift in Sources */,
663FAB9C271D874D00E495F8 /* ActiveButton.swift in Sources */,
9EBDF956291E5E86000A1A05 /* DatabaseFilesLiveKey.swift in Sources */,
9E2F1C842809B606004E65FE /* DebugMenu.swift in Sources */,
9E153A5F2920CE2700112F41 /* MnemonicInterface.swift in Sources */,
9E153A6729210B3B00112F41 /* PasteboardLiveKey.swift in Sources */,
34E0AF0F28DEE4C70034CF37 /* HoldToSendButton.swift in Sources */,
F9C165C02740403600592F76 /* TransactionConfirmationView.swift in Sources */,
0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */,
9E6EF2CD2913B06300CA007B /* NumberFormatterKey.swift in Sources */,
9E39113F2848EC360073DD9A /* WrappedRecoveryPhraseRandomizer.swift in Sources */,
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */,
9E5BF6462821028C00BA3F17 /* WrappedUserDefaults.swift in Sources */,
9E153A70292167FF00112F41 /* ZcashSDKEnvironmentInterface.swift in Sources */,
9EAFEB84280597B700199FC9 /* SecItemInterface.swift in Sources */,
F9971A6B27680E1000A2DB75 /* WalletInfoStore.swift in Sources */,
9EBDF953291E5E86000A1A05 /* DatabaseFilesTestKey.swift in Sources */,
9E7FE0F628327F6F00C374E8 /* ScanUIView.swift in Sources */,
@ -1921,40 +2103,46 @@
0D8A43C6272B129C005A6414 /* WordChipGrid.swift in Sources */,
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */,
9EBDF96D291ECED4000A1A05 /* CaptureDeviceInterface.swift in Sources */,
9EB8638D2922CD4A003D0F8B /* FeedbackGeneratorLiveKey.swift in Sources */,
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */,
9E01F8242833C0D8000EFC57 /* WrappedURIParser.swift in Sources */,
346715A528E2027D0035F7C4 /* CheckCircleStore.swift in Sources */,
9EF1082B29114B93003D8097 /* PasteboardKey.swift in Sources */,
F9C165C22740403600592F76 /* CreateTransactionView.swift in Sources */,
9EBDF955291E5E86000A1A05 /* DatabaseFilesInterface.swift in Sources */,
9EBDF976291F79F9000A1A05 /* DerivationToolLiveKey.swift in Sources */,
F9C165B4274031F600592F76 /* Bindings.swift in Sources */,
2E35F99A27B3E99C00EB79CD /* TextFieldTitleAccessoryButtonStyle.swift in Sources */,
9E153A6929210B3B00112F41 /* PasteboardTestKey.swift in Sources */,
9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */,
9E6713F8289BC58C00A6796F /* BalanceBreakdownView.swift in Sources */,
F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */,
F96B41EB273B50520021B49A /* Strings.swift in Sources */,
9EB863D02923D3FC003D0F8B /* SDKSynchronizerMocks.swift in Sources */,
2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */,
F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */,
F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */,
9EF8139127F191BF0075AF48 /* WrappedWalletStorage.swift in Sources */,
9EB863952922D036003D0F8B /* NumberFormatterTestKey.swift in Sources */,
9EAB46782860A1D2002904A0 /* WalletEvent.swift in Sources */,
9EB8639D29239405003D0F8B /* WalletStorage.swift in Sources */,
0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupView.swift in Sources */,
9E153A622920CE2700112F41 /* MnemonicTestKey.swift in Sources */,
9E69A24D27FB002800A55317 /* WelcomeStore.swift in Sources */,
9EB863C32923C807003D0F8B /* URIParserTest.swift in Sources */,
9E153A6829210B3B00112F41 /* PasteboardInterface.swift in Sources */,
F9C165CB2741AB5D00592F76 /* SendFlowView.swift in Sources */,
9E7FE0DD282D298900C374E8 /* ValidationWord.swift in Sources */,
9E39114B2848EEB90073DD9A /* ZCashSDKEnvironment.swift in Sources */,
9EB8639A2923935B003D0F8B /* WalletStorageTestKey.swift in Sources */,
0D0781C4278750E30083ACD7 /* WelcomeView.swift in Sources */,
9EBDF95F291E657B000A1A05 /* DeeplinkLiveKey.swift in Sources */,
F9971A6527680DFE00A2DB75 /* SettingsStore.swift in Sources */,
9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */,
0D0781C9278776D20083ACD7 /* ZcashSymbol.swift in Sources */,
9EB863C72923C93B003D0F8B /* UserPreferencesStorageLive.swift in Sources */,
2E8719CB27FB09990082C926 /* TransactionAmountTextField.swift in Sources */,
9E6EF2D32913B79A00CA007B /* WalletStorageKey.swift in Sources */,
9E7CB6212874143800A02233 /* AddressDetailsView.swift in Sources */,
9E6713FA289BE0E100A6796F /* ClearBackgroundView.swift in Sources */,
34E5F2F328E46DB700C17E5F /* DiskSpaceChecker.swift in Sources */,
F9C165C42740403600592F76 /* TransactionSentView.swift in Sources */,
9E153A6E292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift in Sources */,
F9971A5927680DDE00A2DB75 /* RequestStore.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1972,14 +2160,14 @@
9EDDEAA42829610D00B4100C /* TransactionAddressInputTests.swift in Sources */,
9E7CB6272874269F00A02233 /* ProfileSnapshotTests.swift in Sources */,
9E92AF0828530EBF007367AD /* View+UIImage.swift in Sources */,
34429C6E28E703CD00F2B929 /* TransactionSendingTests.swift in Sources */,
34429C6E28E703CD00F2B929 /* TransactionSendingSnapshotTests.swift in Sources */,
6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */,
9E94C62328AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift in Sources */,
9E39112E283F91600073DD9A /* ZatoshiTests.swift in Sources */,
9E9ECC9B28589E150099D5A2 /* ImportWalletSnapshotTests.swift in Sources */,
9EDDEAA32829610D00B4100C /* TransactionAmountInputTests.swift in Sources */,
9E7CB6242874246800A02233 /* ProfileTests.swift in Sources */,
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */,
9EAFEB862805A23100199FC9 /* SecItemClientTests.swift in Sources */,
3448CB3728E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift in Sources */,
9E9ECC9828589E150099D5A2 /* WelcomeSnapshotTests.swift in Sources */,
9E7CB6122869882D00A02233 /* WalletEventsSnapshotTests.swift in Sources */,

View File

@ -16,7 +16,7 @@ extension CaptureDeviceClient: TestDependencyKey {
}
extension CaptureDeviceClient {
static let noop = Self(
static let noOp = Self(
isTorchAvailable: { false },
torch: { _ in }
)

View File

@ -20,9 +20,9 @@ struct DatabaseFiles {
case filesPresentCheck
}
private let fileManager: WrappedFileManager
private let fileManager: FileManagerClient
init(fileManager: WrappedFileManager) {
init(fileManager: FileManagerClient) {
self.fileManager = fileManager
}

View File

@ -23,6 +23,6 @@ struct DatabaseFilesClient {
let outputParamsURLFor: (ZcashNetwork) throws -> URL
let pendingDbURLFor: (ZcashNetwork) throws -> URL
let spendParamsURLFor: (ZcashNetwork) throws -> URL
let areDbFilesPresentFor: (ZcashNetwork) throws -> Bool
var areDbFilesPresentFor: (ZcashNetwork) throws -> Bool
let nukeDbFilesFor: (ZcashNetwork) throws -> Void
}

View File

@ -1,35 +0,0 @@
//
// DatabaseFilesMocks.swift
// secant-testnet
//
// Created by Lukáš Korba on 11.11.2022.
//
extension DatabaseFilesClient {
static let throwing = DatabaseFilesClient(
documentsDirectory: {
throw DatabaseFiles.DatabaseFilesError.getDocumentsURL
},
cacheDbURLFor: { _ in
throw DatabaseFiles.DatabaseFilesError.getCacheURL
},
dataDbURLFor: { _ in
throw DatabaseFiles.DatabaseFilesError.getDataURL
},
outputParamsURLFor: { _ in
throw DatabaseFiles.DatabaseFilesError.getOutputParamsURL
},
pendingDbURLFor: { _ in
throw DatabaseFiles.DatabaseFilesError.getPendingURL
},
spendParamsURLFor: { _ in
throw DatabaseFiles.DatabaseFilesError.getSpendParamsURL
},
areDbFilesPresentFor: { _ in
throw DatabaseFiles.DatabaseFilesError.filesPresentCheck
},
nukeDbFilesFor: { _ in
throw DatabaseFiles.DatabaseFilesError.nukeFiles
}
)
}

View File

@ -5,18 +5,38 @@
// Created by Lukáš Korba on 11.11.2022.
//
import Foundation
import ComposableArchitecture
import XCTestDynamicOverlay
extension DatabaseFilesClient: TestDependencyKey {
static let testValue = Self(
documentsDirectory: XCTUnimplemented("\(Self.self).documentsDirectory"),
cacheDbURLFor: XCTUnimplemented("\(Self.self).cacheDbURLFor"),
dataDbURLFor: XCTUnimplemented("\(Self.self).dataDbURLFor"),
outputParamsURLFor: XCTUnimplemented("\(Self.self).outputParamsURLFor"),
pendingDbURLFor: XCTUnimplemented("\(Self.self).pendingDbURLFor"),
spendParamsURLFor: XCTUnimplemented("\(Self.self).spendParamsURLFor"),
documentsDirectory: XCTUnimplemented("\(Self.self).documentsDirectory", placeholder: .emptyURL),
cacheDbURLFor: XCTUnimplemented("\(Self.self).cacheDbURLFor", placeholder: .emptyURL),
dataDbURLFor: XCTUnimplemented("\(Self.self).dataDbURLFor", placeholder: .emptyURL),
outputParamsURLFor: XCTUnimplemented("\(Self.self).outputParamsURLFor", placeholder: .emptyURL),
pendingDbURLFor: XCTUnimplemented("\(Self.self).pendingDbURLFor", placeholder: .emptyURL),
spendParamsURLFor: XCTUnimplemented("\(Self.self).spendParamsURLFor", placeholder: .emptyURL),
areDbFilesPresentFor: XCTUnimplemented("\(Self.self).areDbFilesPresentFor", placeholder: false),
nukeDbFilesFor: XCTUnimplemented("\(Self.self).nukeDbFilesFor")
)
}
extension URL {
/// The `DatabaseFilesClient` API returns an instance of the URL or throws an error.
/// In order to use placeholders for the URL we need a URL instance, hence `emptyURL` and force unwrapp.
static let emptyURL = URL(string: "http://empty.url")!// swiftlint:disable:this force_unwrapping
}
extension DatabaseFilesClient {
static let noOp = Self(
documentsDirectory: { .emptyURL },
cacheDbURLFor: { _ in .emptyURL },
dataDbURLFor: { _ in .emptyURL },
outputParamsURLFor: { _ in .emptyURL },
pendingDbURLFor: { _ in .emptyURL },
spendParamsURLFor: { _ in .emptyURL },
areDbFilesPresentFor: { _ in false },
nukeDbFilesFor: { _ in }
)
}

View File

@ -11,7 +11,7 @@ import ZcashLightClientKit
extension DerivationToolClient: DependencyKey {
static let liveValue = DerivationToolClient.live()
static func live(derivationTool: DerivationTool = DerivationTool(networkType: .mainnet)) -> Self {
static func live(derivationTool: DerivationTool = DerivationTool(networkType: .testnet)) -> Self {
Self(
deriveViewingKeys: { seed, numberOfAccounts in
try derivationTool.deriveViewingKeys(seed: seed, numberOfAccounts: numberOfAccounts)

View File

@ -43,7 +43,7 @@ extension DerivationToolClient {
}
extension DerivationToolClient {
static let noop = Self(
static let noOp = Self(
deriveViewingKeys: { _, _ in [] },
deriveViewingKey: { _ in "" },
deriveSpendingKeys: { _, _ in [] },

View File

@ -0,0 +1,21 @@
//
// FeedbackGeneratorInterface.swift
// secant-testnet
//
// Created by Lukáš Korba on 14.11.2022.
//
import ComposableArchitecture
extension DependencyValues {
var feedbackGenerator: FeedbackGeneratorClient {
get { self[FeedbackGeneratorClient.self] }
set { self[FeedbackGeneratorClient.self] = newValue }
}
}
struct FeedbackGeneratorClient {
let generateSuccessFeedback: () -> Void
let generateWarningFeedback: () -> Void
let generateErrorFeedback: () -> Void
}

View File

@ -0,0 +1,17 @@
//
// FeedbackGeneratorLiveKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 14.11.2022.
//
import UIKit
import ComposableArchitecture
extension FeedbackGeneratorClient: DependencyKey {
static let liveValue = Self(
generateSuccessFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.success) },
generateWarningFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.warning) },
generateErrorFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.error) }
)
}

View File

@ -0,0 +1,25 @@
//
// FeedbackGeneratorTestKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 14.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension FeedbackGeneratorClient: TestDependencyKey {
static let testValue = Self(
generateSuccessFeedback: XCTUnimplemented("\(Self.self).generateSuccessFeedback"),
generateWarningFeedback: XCTUnimplemented("\(Self.self).generateWarningFeedback"),
generateErrorFeedback: XCTUnimplemented("\(Self.self).generateErrorFeedback")
)
}
extension FeedbackGeneratorClient {
static let noOp = Self(
generateSuccessFeedback: { },
generateWarningFeedback: { },
generateErrorFeedback: { }
)
}

View File

@ -1,20 +0,0 @@
//
// FeedbackGeneratorKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 02.11.2022.
//
import ComposableArchitecture
private enum FeedbackGeneratorKey: DependencyKey {
static let liveValue = WrappedFeedbackGenerator.haptic
static let testValue = WrappedFeedbackGenerator.silent
}
extension DependencyValues {
var feedbackGenerator: WrappedFeedbackGenerator {
get { self[FeedbackGeneratorKey.self] }
set { self[FeedbackGeneratorKey.self] = newValue }
}
}

View File

@ -0,0 +1,14 @@
//
// FileManagerClient.swift
// secant-testnet
//
// Created by Lukáš Korba on 07.04.2022.
//
import Foundation
struct FileManagerClient {
let url: (FileManager.SearchPathDirectory, FileManager.SearchPathDomainMask, URL?, Bool) throws -> URL
let fileExists: (String) -> Bool
let removeItem: (URL) throws -> Void
}

View File

@ -1,20 +1,14 @@
//
// WrappedFileManager.swift
// FileManagerLive.swift
// secant-testnet
//
// Created by Lukáš Korba on 07.04.2022.
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
struct WrappedFileManager {
let url: (FileManager.SearchPathDirectory, FileManager.SearchPathDomainMask, URL?, Bool) throws -> URL
let fileExists: (String) -> Bool
let removeItem: (URL) throws -> Void
}
extension WrappedFileManager {
static let live = WrappedFileManager(
extension FileManagerClient {
static let live = FileManagerClient(
url: { searchPathDirectory, searchPathDomainMask, appropriateForURL, shouldCreate in
try FileManager.default.url(for: searchPathDirectory, in: searchPathDomainMask, appropriateFor: appropriateForURL, create: shouldCreate)
},

View File

@ -0,0 +1,17 @@
//
// FileManagerTest.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension FileManagerClient: TestDependencyKey {
static let testValue = Self(
url: XCTUnimplemented("\(Self.self).url"),
fileExists: XCTUnimplemented("\(Self.self).fileExists", placeholder: false),
removeItem: XCTUnimplemented("\(Self.self).removeItem")
)
}

View File

@ -0,0 +1,28 @@
//
// MnemonicInterface.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
extension DependencyValues {
var mnemonic: MnemonicClient {
get { self[MnemonicClient.self] }
set { self[MnemonicClient.self] = newValue }
}
}
struct MnemonicClient {
/// Random 24 words mnemonic phrase
var randomMnemonic: () throws -> String
/// Random 24 words mnemonic phrase as array of words
var randomMnemonicWords: () throws -> [String]
/// Generate deterministic seed from mnemonic phrase
var toSeed: (String) throws -> [UInt8]
/// Get this mnemonic phrase as array of words
var asWords: (String) throws -> [String]
/// Validates whether the given mnemonic is correct
var isValid: (String) throws -> Void
}

View File

@ -0,0 +1,31 @@
//
// MnemonicLiveKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
import MnemonicSwift
extension MnemonicClient: DependencyKey {
static let liveValue = Self(
randomMnemonic: {
try Mnemonic.generateMnemonic(strength: 256)
},
randomMnemonicWords: {
try Mnemonic.generateMnemonic(strength: 256).components(separatedBy: " ")
},
toSeed: { mnemonic in
let data = try Mnemonic.deterministicSeedBytes(from: mnemonic)
return [UInt8](data)
},
asWords: { mnemonic in
mnemonic.components(separatedBy: " ")
},
isValid: { mnemonic in
try Mnemonic.validate(mnemonic: mnemonic)
}
)
}

View File

@ -1,48 +1,14 @@
//
// WrappedMnemonic.swift
// MnemonicMocks.swift
// secant-testnet
//
// Created by Lukáš Korba on 03/09/2022.
// Created by Lukáš Korba on 13.11.2022.
//
import Foundation
import MnemonicSwift
struct WrappedMnemonic {
/// Random 24 words mnemonic phrase
var randomMnemonic: () throws -> String
/// Random 24 words mnemonic phrase as array of words
var randomMnemonicWords: () throws -> [String]
/// Generate deterministic seed from mnemonic phrase
var toSeed: (String) throws -> [UInt8]
/// Get this mnemonic phrase as array of words
var asWords: (String) throws -> [String]
/// Validates whether the given mnemonic is correct
var isValid: (String) throws -> Void
}
extension WrappedMnemonic {
static let live = WrappedMnemonic(
randomMnemonic: {
try Mnemonic.generateMnemonic(strength: 256)
},
randomMnemonicWords: {
try Mnemonic.generateMnemonic(strength: 256).components(separatedBy: " ")
},
toSeed: { mnemonic in
let data = try Mnemonic.deterministicSeedBytes(from: mnemonic)
return [UInt8](data)
},
asWords: { mnemonic in
mnemonic.components(separatedBy: " ")
},
isValid: { mnemonic in
try Mnemonic.validate(mnemonic: mnemonic)
}
)
static let mock = WrappedMnemonic(
extension MnemonicClient {
static let mock = MnemonicClient(
randomMnemonic: {
"""
still champion voice habit trend flight \

View File

@ -0,0 +1,29 @@
//
// MnemonicTestKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension MnemonicClient: TestDependencyKey {
static let testValue = Self(
randomMnemonic: XCTUnimplemented("\(Self.self).randomMnemonic", placeholder: ""),
randomMnemonicWords: XCTUnimplemented("\(Self.self).randomMnemonicWords", placeholder: []),
toSeed: XCTUnimplemented("\(Self.self).toSeed", placeholder: []),
asWords: XCTUnimplemented("\(Self.self).asWords", placeholder: []),
isValid: XCTUnimplemented("\(Self.self).isValid")
)
}
extension MnemonicClient {
static let noOp = Self(
randomMnemonic: { "" },
randomMnemonicWords: { [] },
toSeed: { _ in [] },
asWords: { _ in [] },
isValid: { _ in }
)
}

View File

@ -1,20 +0,0 @@
//
// MnemonicKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 03.11.2022.
//
import ComposableArchitecture
private enum MnemonicKey: DependencyKey {
static let liveValue = WrappedMnemonic.live
static let testValue = WrappedMnemonic.mock
}
extension DependencyValues {
var mnemonic: WrappedMnemonic {
get { self[MnemonicKey.self] }
set { self[MnemonicKey.self] = newValue }
}
}

View File

@ -1,22 +0,0 @@
//
// NewRecoveryPhrase.swift
// secant-testnet
//
// Created by Lukáš Korba on 01.11.2022.
//
import ComposableArchitecture
// TODO: Ensure that sensitive information can't be logged intentionally or by accident #444.
// https://github.com/zcash/secant-ios-wallet/issues/444
private enum NewRecoveryPhraseKey: DependencyKey {
static let liveValue = { RecoveryPhrase.init(words: RecoveryPhrase.placeholder.words) }
static let testValue = { RecoveryPhrase.init(words: RecoveryPhrase.placeholder.words) }
}
extension DependencyValues {
var newRecoveryPhrase: () -> RecoveryPhrase {
get { self[NewRecoveryPhraseKey.self] }
set { self[NewRecoveryPhraseKey.self] = newValue }
}
}

View File

@ -0,0 +1,12 @@
//
// NotificationCenter.swift
// secant-testnet
//
// Created by Lukáš Korba on 04.05.2022.
//
import Foundation
struct NotificationCenterClient {
let publisherFor: (Notification.Name) -> NotificationCenter.Publisher?
}

View File

@ -0,0 +1,14 @@
//
// NotificationCenterLive.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
extension NotificationCenterClient {
static let live = NotificationCenterClient(
publisherFor: { NotificationCenter.default.publisher(for: $0) }
)
}

View File

@ -0,0 +1,21 @@
//
// NotificationCenterTest.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension NotificationCenterClient: TestDependencyKey {
static let testValue = Self(
publisherFor: XCTUnimplemented("\(Self.self).publisherFor", placeholder: nil)
)
}
extension NotificationCenterClient {
static let noOp = NotificationCenterClient(
publisherFor: { _ in nil }
)
}

View File

@ -0,0 +1,21 @@
//
// AppVersionInterface.swift
// secant-testnet
//
// Created by Lukáš Korba on 12.11.2022.
//
import Foundation
import ComposableArchitecture
extension DependencyValues {
var numberFormatter: NumberFormatterClient {
get { self[NumberFormatterClient.self] }
set { self[NumberFormatterClient.self] = newValue }
}
}
struct NumberFormatterClient {
var string: (NSDecimalNumber) -> String?
var number: (String) -> NSNumber?
}

View File

@ -1,11 +1,12 @@
//
// WrappedNumberFormatter.swift
// NumberFormatterLiveKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 02.06.2022.
// Created by Lukáš Korba on 14.11.2022.
//
import Foundation
import ComposableArchitecture
extension NumberFormatter {
static let zcashNumberFormatter: NumberFormatter = {
@ -27,12 +28,9 @@ extension NumberFormatter {
}()
}
struct WrappedNumberFormatter {
let string: (NSDecimalNumber) -> String?
let number: (String) -> NSNumber?
}
extension NumberFormatterClient: DependencyKey {
static let liveValue = NumberFormatterClient.live()
extension WrappedNumberFormatter {
static func live(numberFormatter: NumberFormatter = NumberFormatter.zcashNumberFormatter) -> Self {
Self(
string: { numberFormatter.string(from: $0) },

View File

@ -0,0 +1,23 @@
//
// NumberFormatterTestKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 14.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension NumberFormatterClient: TestDependencyKey {
static let testValue = Self(
string: XCTUnimplemented("\(Self.self).string", placeholder: nil),
number: XCTUnimplemented("\(Self.self).number", placeholder: nil)
)
}
extension NumberFormatterClient {
static let noOp = Self(
string: { _ in nil },
number: { _ in nil }
)
}

View File

@ -1,20 +0,0 @@
//
// NumberFormatterKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 03.11.2022.
//
import ComposableArchitecture
private enum NumberFormatterKey: DependencyKey {
static let liveValue = WrappedNumberFormatter.live()
static let testValue = WrappedNumberFormatter.live()
}
extension DependencyValues {
var numberFormatter: WrappedNumberFormatter {
get { self[NumberFormatterKey.self] }
set { self[NumberFormatterKey.self] = newValue }
}
}

View File

@ -0,0 +1,20 @@
//
// PasteboardInterface.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
extension DependencyValues {
var pasteboard: PasteboardClient {
get { self[PasteboardClient.self] }
set { self[PasteboardClient.self] = newValue }
}
}
struct PasteboardClient {
let setString: (String) -> Void
let getString: () -> String?
}

View File

@ -0,0 +1,16 @@
//
// PasteboardLiveKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
import UIKit
extension PasteboardClient: DependencyKey {
static let liveValue = Self(
setString: { UIPasteboard.general.string = $0 },
getString: { UIPasteboard.general.string }
)
}

View File

@ -0,0 +1,26 @@
//
// PasteboardTestKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension PasteboardClient: TestDependencyKey {
static let testValue = Self(
setString: XCTUnimplemented("\(Self.self).setString"),
getString: XCTUnimplemented("\(Self.self).getString", placeholder: "")
)
private struct TestPasteboard {
static var general = TestPasteboard()
var string: String?
}
static let testPasteboard = Self(
setString: { TestPasteboard.general.string = $0 },
getString: { TestPasteboard.general.string }
)
}

View File

@ -1,22 +0,0 @@
//
// PasteboardKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 01.11.2022.
//
import ComposableArchitecture
// TODO: Ensure that sensitive information can't be logged intentionally or by accident #444.
// https://github.com/zcash/secant-ios-wallet/issues/444
private enum PasteboardKey: DependencyKey {
static let liveValue = WrappedPasteboard.live
static let testValue = WrappedPasteboard.test
}
extension DependencyValues {
var pasteboard: WrappedPasteboard {
get { self[PasteboardKey.self] }
set { self[PasteboardKey.self] = newValue }
}
}

View File

@ -6,7 +6,6 @@
//
import Foundation
import ComposableArchitecture
struct RecoveryPhraseRandomizer {
func random(phrase: RecoveryPhrase) -> RecoveryPhraseValidationFlowReducer.State {
@ -22,20 +21,8 @@ struct RecoveryPhraseRandomizer {
}
func randomIndices() -> [Int] {
return (0..<RecoveryPhraseValidationFlowReducer.State.phraseChunks).map { _ in
(0..<RecoveryPhraseValidationFlowReducer.State.phraseChunks).map { _ in
Int.random(in: 0 ..< RecoveryPhraseValidationFlowReducer.State.wordGroupSize)
}
}
}
private enum RecoveryPhraseRandomKey: DependencyKey {
static let liveValue = WrappedRecoveryPhraseRandomizer.live
static let testValue = WrappedRecoveryPhraseRandomizer.live
}
extension DependencyValues {
var randomPhrase: WrappedRecoveryPhraseRandomizer {
get { self[RecoveryPhraseRandomKey.self] }
set { self[RecoveryPhraseRandomKey.self] = newValue }
}
}

View File

@ -0,0 +1,19 @@
//
// RecoveryPhraseRandomizerInterface.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import ComposableArchitecture
extension DependencyValues {
var randomRecoveryPhrase: RecoveryPhraseRandomizerClient {
get { self[RecoveryPhraseRandomizerClient.self] }
set { self[RecoveryPhraseRandomizerClient.self] = newValue }
}
}
struct RecoveryPhraseRandomizerClient {
let random: (RecoveryPhrase) -> RecoveryPhraseValidationFlowReducer.State
}

View File

@ -0,0 +1,15 @@
//
// RecoveryPhraseRandomizerLiveKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
import ComposableArchitecture
extension RecoveryPhraseRandomizerClient: DependencyKey {
static let liveValue = Self(
random: { RecoveryPhraseRandomizer().random(phrase: $0) }
)
}

View File

@ -0,0 +1,15 @@
//
// RecoveryPhraseRandomizerTestKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension RecoveryPhraseRandomizerClient: TestDependencyKey {
static let testValue = Self(
random: XCTUnimplemented("\(Self.self).random", placeholder: .placeholder)
)
}

View File

@ -0,0 +1,86 @@
//
// SDKSynchronizerClient.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.04.2022.
//
import Foundation
import Combine
import ComposableArchitecture
import ZcashLightClientKit
extension DependencyValues {
var sdkSynchronizer: SDKSynchronizerClient {
get { self[SDKSynchronizerDependency.self] }
set { self[SDKSynchronizerDependency.self] = newValue }
}
}
enum SDKSynchronizerState: Equatable {
case unknown
case transactionsUpdated
case started
case progressUpdated
case statusWillUpdate
case synced
case stopped
case disconnected
case syncing
case downloading
case validating
case scanning
case enhancing
case fetching
case minedTransaction
case foundTransactions
case failed
case connectionStateChanged
}
protocol SDKSynchronizerClient {
var notificationCenter: NotificationCenterClient { get }
var synchronizer: SDKSynchronizer? { get }
var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never> { get }
var walletBirthday: BlockHeight? { get }
var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState? { get }
func prepareWith(initializer: Initializer) throws
func start(retry: Bool) throws
func stop()
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?)
func statusSnapshot() -> SyncStatusSnapshot
func rewind(_ policy: RewindPolicy) throws
func getShieldedBalance() -> WalletBalance?
func getTransparentBalance() -> WalletBalance?
func getAllClearedTransactions() -> Effect<[WalletEvent], Never>
func getAllPendingTransactions() -> Effect<[WalletEvent], Never>
func getAllTransactions() -> Effect<[WalletEvent], Never>
func getTransparentAddress(account: Int) -> TransparentAddress?
func getShieldedAddress(account: Int) -> SaplingShieldedAddress?
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never>
}
extension SDKSynchronizerClient {
func start() throws {
try start(retry: false)
}
func getTransparentAddress() -> TransparentAddress? {
getTransparentAddress(account: 0)
}
func getShieldedAddress() -> SaplingShieldedAddress? {
getShieldedAddress(account: 0)
}
}

View File

@ -0,0 +1,197 @@
//
// SDKSynchronizerLive.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
import Combine
import ComposableArchitecture
import ZcashLightClientKit
enum SDKSynchronizerDependency: DependencyKey {
static let liveValue: SDKSynchronizerClient = LiveSDKSynchronizerClient()
}
class LiveSDKSynchronizerClient: SDKSynchronizerClient {
private var cancellables: [AnyCancellable] = []
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
private(set) var notificationCenter: NotificationCenterClient
private(set) var walletBirthday: BlockHeight?
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
init(notificationCenter: NotificationCenterClient = .live) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(.unknown)
}
deinit {
synchronizer?.stop()
}
func prepareWith(initializer: Initializer) throws {
synchronizer = try SDKSynchronizer(initializer: initializer)
notificationCenter.publisherFor(.synchronizerStarted)?
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.synchronizerStarted() }
.store(in: &cancellables)
notificationCenter.publisherFor(.synchronizerSynced)?
.receive(on: DispatchQueue.main)
.sink { [weak self] output in
let synchronizerState = output.userInfo?[SDKSynchronizer.NotificationKeys.synchronizerState] as? SDKSynchronizer.SynchronizerState
self?.synchronizerSynced(synchronizerState)
}
.store(in: &cancellables)
notificationCenter.publisherFor(.synchronizerProgressUpdated)?
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.synchronizerProgressUpdated() }
.store(in: &cancellables)
notificationCenter.publisherFor(.synchronizerStopped)?
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.synchronizerStopped() }
.store(in: &cancellables)
try synchronizer?.prepare()
walletBirthday = initializer.walletBirthday
}
func start(retry: Bool) throws {
try synchronizer?.start(retry: retry)
}
func stop() {
synchronizer?.stop()
}
func synchronizerStarted() {
stateChanged.send(.started)
}
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) {
stateChanged.send(.synced)
latestScannedSynchronizerState = synchronizerState
}
func synchronizerProgressUpdated() {
stateChanged.send(.progressUpdated)
}
func synchronizerStopped() {
stateChanged.send(.stopped)
}
func statusSnapshot() -> SyncStatusSnapshot {
guard let synchronizer = synchronizer else {
return .default
}
return SyncStatusSnapshot.snapshotFor(state: synchronizer.status)
}
func rewind(_ policy: RewindPolicy) throws {
try synchronizer?.rewind(policy)
}
func getShieldedBalance() -> WalletBalance? {
latestScannedSynchronizerState?.shieldedBalance
}
func getTransparentBalance() -> WalletBalance? {
latestScannedSynchronizerState?.transparentBalance
}
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
if let clearedTransactions = try? synchronizer?.allClearedTransactions() {
return Effect(value: clearedTransactions.map {
let transaction = TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil))
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
})
}
return .none
}
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
if let pendingTransactions = try? synchronizer?.allPendingTransactions(),
let syncedBlockHeight = synchronizer?.latestScannedHeight {
return Effect(value: pendingTransactions.map {
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
})
}
return .none
}
func getAllTransactions() -> Effect<[WalletEvent], Never> {
if let pendingTransactions = try? synchronizer?.allPendingTransactions(),
let clearedTransactions = try? synchronizer?.allClearedTransactions(),
let syncedBlockHeight = synchronizer?.latestScannedHeight {
let clearedTxs: [WalletEvent] = clearedTransactions.map {
let transaction = TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil))
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
let pendingTxs: [WalletEvent] = pendingTransactions.map {
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
let txs = clearedTxs.filter { cleared in
pendingTxs.first { pending in
pending.id == cleared.id
} == nil
}
return .merge(
Effect(value: txs),
Effect(value: pendingTxs)
)
.flatMap(Publishers.Sequence.init(sequence:))
.collect()
.eraseToEffect()
}
return .none
}
func getTransparentAddress(account: Int) -> TransparentAddress? {
synchronizer?.getTransparentAddress(accountIndex: account)
}
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? {
synchronizer?.getShieldedAddress(accountIndex: account)
}
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never> {
Deferred {
Future { [weak self] promise in
self?.synchronizer?.sendToAddress(
spendingKey: spendingKey,
zatoshi: zatoshi,
toAddress: recipientAddress,
memo: memo,
from: account) { result in
switch result {
case .failure(let error as NSError):
promise(.failure(error))
case .success(let pendingTx):
promise(.success(TransactionState(pendingTransaction: pendingTx)))
}
}
}
}
.mapError { $0 as NSError }
.catchToEffect()
}
}

View File

@ -0,0 +1,140 @@
//
// SDKSynchronizerMocks.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
import Combine
import ComposableArchitecture
import ZcashLightClientKit
extension SDKSynchronizerDependency {
static let mock: SDKSynchronizerClient = MockSDKSynchronizerClient()
}
class MockSDKSynchronizerClient: SDKSynchronizerClient {
private var cancellables: [AnyCancellable] = []
private(set) var notificationCenter: NotificationCenterClient
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
private(set) var walletBirthday: BlockHeight?
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
init(notificationCenter: NotificationCenterClient = .noOp) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(.unknown)
}
func prepareWith(initializer: Initializer) throws { }
func start(retry: Bool) throws { }
func stop() { }
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) { }
func statusSnapshot() -> SyncStatusSnapshot { .default }
func rewind(_ policy: RewindPolicy) throws { }
func getShieldedBalance() -> WalletBalance? {
WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000))
}
func getTransparentBalance() -> WalletBalance? {
WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000))
}
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(
date: 1651039606,
amount: Zatoshi(6),
status: .paid(success: false),
uuid: "ff66"
),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.amount.amount > 5 ? .pending : $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllTransactions() -> Effect<[WalletEvent], Never> {
return .merge(
getAllClearedTransactions(),
getAllPendingTransactions()
)
.flatMap(Publishers.Sequence.init(sequence:))
.collect()
.eraseToEffect()
}
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? { "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8" }
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never> {
let transactionState = TransactionState(
expirationHeight: 40,
memo: memo,
minedHeight: 50,
shielded: true,
zAddress: "tteafadlamnelkqe",
fee: Zatoshi(10),
id: "id",
status: .paid(success: true),
timestamp: 1234567,
zecAmount: Zatoshi(10)
)
return Effect(value: Result.success(transactionState))
}
}

View File

@ -0,0 +1,180 @@
//
// SDKSynchronizerTest.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
import Combine
import ComposableArchitecture
import ZcashLightClientKit
extension SDKSynchronizerDependency: TestDependencyKey {
static let testValue: SDKSynchronizerClient = NoopSDKSynchronizer()
}
class NoopSDKSynchronizer: SDKSynchronizerClient {
private(set) var notificationCenter: NotificationCenterClient
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
private(set) var walletBirthday: BlockHeight?
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
init(notificationCenter: NotificationCenterClient = .noOp) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(.unknown)
}
func prepareWith(initializer: Initializer) throws { }
func start(retry: Bool) throws { }
func stop() { }
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) { }
func statusSnapshot() -> SyncStatusSnapshot { .default }
func rewind(_ policy: RewindPolicy) throws { }
func getShieldedBalance() -> WalletBalance? { nil }
func getTransparentBalance() -> WalletBalance? { nil }
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) }
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) }
func getAllTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) }
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? { nil }
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never> {
Effect(value: Result.failure(SynchronizerError.criticalError as NSError))
}
func updateStateChanged(_ newState: SDKSynchronizerState) {
stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(newState)
}
}
class T2estSDKSynchronizerClient: SDKSynchronizerClient {
private(set) var blockProcessor: CompactBlockProcessor?
private(set) var notificationCenter: NotificationCenterClient
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
private(set) var walletBirthday: BlockHeight?
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
init(notificationCenter: NotificationCenterClient = .noOp) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(.unknown)
}
func prepareWith(initializer: Initializer) throws { }
func start(retry: Bool) throws { }
func stop() { }
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) { }
func statusSnapshot() -> SyncStatusSnapshot { .default }
func rewind(_ policy: RewindPolicy) throws { }
func getShieldedBalance() -> WalletBalance? { nil }
func getTransparentBalance() -> WalletBalance? { nil }
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(
date: 1651039606,
amount: Zatoshi(6),
status: .paid(success: false),
uuid: "ff66"
),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.amount.amount > 5 ? .pending : $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllTransactions() -> Effect<[WalletEvent], Never> {
return .merge(
getAllClearedTransactions(),
getAllPendingTransactions()
)
.flatMap(Publishers.Sequence.init(sequence:))
.collect()
.eraseToEffect()
}
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? { "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8" }
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never> {
return Effect(value: Result.failure(SynchronizerError.criticalError as NSError))
}
func updateStateChanged(_ newState: SDKSynchronizerState) {
stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(newState)
}
}

View File

@ -1,20 +0,0 @@
//
// SDKSynchronizerKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 03.11.2022.
//
import ComposableArchitecture
private enum SDKSynchronizerKey: DependencyKey {
static let liveValue: WrappedSDKSynchronizer = LiveWrappedSDKSynchronizer()
static let testValue: WrappedSDKSynchronizer = MockWrappedSDKSynchronizer()
}
extension DependencyValues {
var sdkSynchronizer: WrappedSDKSynchronizer {
get { self[SDKSynchronizerKey.self] }
set { self[SDKSynchronizerKey.self] = newValue }
}
}

View File

@ -0,0 +1,16 @@
//
// SecItemClient.swift
// secant-testnet
//
// Created by Lukáš Korba on 12.04.2022.
//
import Foundation
import Security
struct SecItemClient {
let copyMatching: (CFDictionary, inout CFTypeRef?) -> OSStatus
let add: (CFDictionary, inout CFTypeRef?) -> OSStatus
let update: (CFDictionary, CFDictionary) -> OSStatus
let delete: (CFDictionary) -> OSStatus
}

View File

@ -1,22 +1,15 @@
//
// WrappedSecItem.swift
// SecItemLive.swift
// secant-testnet
//
// Created by Lukáš Korba on 12.04.2022.
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
import Security
struct WrappedSecItem {
let copyMatching: (CFDictionary, inout CFTypeRef?) -> OSStatus
let add: (CFDictionary, inout CFTypeRef?) -> OSStatus
let update: (CFDictionary, CFDictionary) -> OSStatus
let delete: (CFDictionary) -> OSStatus
}
extension WrappedSecItem {
static let live = WrappedSecItem(
extension SecItemClient {
static let live = SecItemClient(
copyMatching: { query, result in
SecItemCopyMatching(query, &result)
},

View File

@ -6,7 +6,6 @@
//
import Foundation
import ComposableArchitecture
struct URIParser {
enum URIParserError: Error { }
@ -21,15 +20,3 @@ struct URIParser {
try derivationTool.isValidZcashAddress(uri)
}
}
private enum URIParserKey: DependencyKey {
static let liveValue = WrappedURIParser.live()
static let testValue = WrappedURIParser.live()
}
extension DependencyValues {
var uriParser: WrappedURIParser {
get { self[URIParserKey.self] }
set { self[URIParserKey.self] = newValue }
}
}

View File

@ -0,0 +1,21 @@
//
// URIParserClient.swift
// secant-testnet
//
// Created by Lukáš Korba on 17.05.2022.
//
import Foundation
import ComposableArchitecture
extension DependencyValues {
var uriParser: URIParserClient {
get { self[URIParserClient.self] }
set { self[URIParserClient.self] = newValue }
}
}
struct URIParserClient {
var isValidURI: (String) throws -> Bool
}

View File

@ -1,17 +1,15 @@
//
// WrappedURIParser.swift
// URIParserLive.swift
// secant-testnet
//
// Created by Lukáš Korba on 17.05.2022.
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
import ComposableArchitecture
struct WrappedURIParser {
let isValidURI: (String) throws -> Bool
}
extension WrappedURIParser {
extension URIParserClient: DependencyKey {
static let liveValue = URIParserClient.live()
static func live(uriParser: URIParser = URIParser(derivationTool: .live())) -> Self {
Self(
isValidURI: { uri in
@ -20,4 +18,3 @@ extension WrappedURIParser {
)
}
}

View File

@ -0,0 +1,15 @@
//
// URIParserTest.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension URIParserClient: TestDependencyKey {
static let testValue = Self(
isValidURI: XCTUnimplemented("\(Self.self).isValidURI", placeholder: false)
)
}

View File

@ -0,0 +1,28 @@
//
// UserDefaultsInterface.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import Foundation
import ComposableArchitecture
/// `UserDefaults` is thread-safe class. Because of that we can mark it as `Sendable` on our own. If it's marked as `Sendable` in `Foundation` in
/// future we can simply remove this line and be ok. This is probably simpliest and easiest way how to fix warnings about `UserDefaults` not being
/// sendable.
extension UserDefaults: @unchecked Sendable { }
extension DependencyValues {
var userDefaults: UserDefaultsClient {
get { self[UserDefaultsClient.self] }
set { self[UserDefaultsClient.self] = newValue }
}
}
struct UserDefaultsClient {
let objectForKey: @Sendable (String) -> Any?
let remove: @Sendable (String) async -> Void
let setValue: @Sendable (Any?, String) async -> Void
let synchronize: @Sendable () async -> Bool
}

View File

@ -0,0 +1,22 @@
//
// UserDefaultsLiveKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import Foundation
import ComposableArchitecture
extension UserDefaultsClient: DependencyKey {
static let liveValue = UserDefaultsClient.live()
static func live(userDefaults: UserDefaults = .standard) -> Self {
Self(
objectForKey: { userDefaults.object(forKey: $0) },
remove: { userDefaults.removeObject(forKey: $0) },
setValue: { userDefaults.set($0, forKey: $1) },
synchronize: { userDefaults.synchronize() }
)
}
}

View File

@ -0,0 +1,27 @@
//
// UserDefaultsTestKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension UserDefaultsClient: TestDependencyKey {
static let testValue = Self(
objectForKey: XCTUnimplemented("\(Self.self).objectForKey", placeholder: nil),
remove: XCTUnimplemented("\(Self.self).remove"),
setValue: XCTUnimplemented("\(Self.self).setValue"),
synchronize: XCTUnimplemented("\(Self.self).synchronize", placeholder: false)
)
}
extension UserDefaultsClient {
static let noOp = Self(
objectForKey: { _ in nil },
remove: { _ in },
setValue: { _, _ in },
synchronize: { false }
)
}

View File

@ -26,7 +26,7 @@ struct UserPreferencesStorage {
private let recoveryPhraseTestCompleted: Bool
private let sessionAutoshielded: Bool
private let userDefaults: WrappedUserDefaults
private let userDefaults: UserDefaultsClient
init(
appSessionFrom: TimeInterval,
@ -34,7 +34,7 @@ struct UserPreferencesStorage {
fiatConvertion: Bool,
recoveryPhraseTestCompleted: Bool,
sessionAutoshielded: Bool,
userDefaults: WrappedUserDefaults
userDefaults: UserDefaultsClient
) {
self.appSessionFrom = appSessionFrom
self.convertedCurrency = convertedCurrency
@ -107,23 +107,3 @@ private extension UserPreferencesStorage {
_ = await userDefaults.synchronize()
}
}
extension UserPreferencesStorage {
static let live = UserPreferencesStorage(
appSessionFrom: Date().timeIntervalSince1970,
convertedCurrency: "USD",
fiatConvertion: true,
recoveryPhraseTestCompleted: false,
sessionAutoshielded: true,
userDefaults: .live()
)
static let mock = UserPreferencesStorage(
appSessionFrom: 1651039606.0,
convertedCurrency: "USD",
fiatConvertion: true,
recoveryPhraseTestCompleted: false,
sessionAutoshielded: true,
userDefaults: .mock
)
}

View File

@ -0,0 +1,19 @@
//
// UserPreferencesStorageLive.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
extension UserPreferencesStorage {
static let live = UserPreferencesStorage(
appSessionFrom: Date().timeIntervalSince1970,
convertedCurrency: "USD",
fiatConvertion: true,
recoveryPhraseTestCompleted: false,
sessionAutoshielded: true,
userDefaults: .live()
)
}

View File

@ -0,0 +1,19 @@
//
// UserPreferencesStorageMocks.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
extension UserPreferencesStorage {
static let mock = UserPreferencesStorage(
appSessionFrom: 1651039606.0,
convertedCurrency: "USD",
fiatConvertion: true,
recoveryPhraseTestCompleted: false,
sessionAutoshielded: true,
userDefaults: .noOp
)
}

View File

@ -35,10 +35,10 @@ struct WalletStorage {
case unsupportedLanguage(MnemonicLanguageType)
}
private let secItem: WrappedSecItem
private let secItem: SecItemClient
var zcashStoredWalletPrefix = ""
init(secItem: WrappedSecItem) {
init(secItem: SecItemClient) {
self.secItem = secItem
}

View File

@ -1,21 +1,28 @@
//
// WrappedWalletStorage.swift
// WalletStorageInterface.swift
// secant-testnet
//
// Created by Lukáš Korba on 03/25/2022.
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
import ComposableArchitecture
import MnemonicSwift
import ZcashLightClientKit
/// The `WrappedWalletStorage` is a wrapper around the `WalletStorage` type that allows for easy testing.
extension DependencyValues {
var walletStorage: WalletStorageClient {
get { self[WalletStorageClient.self] }
set { self[WalletStorageClient.self] = newValue }
}
}
/// The `WalletStorageClient` is a wrapper around the `WalletStorage` type that allows for easy testing.
/// The `WalletStorage` Type is comprised of static functions that take and produce data. In order
/// to easily produce test data, all of these static functions have been wrapped in function
/// properties that live on the `WrappedWalletStorage` type. Because of this, you can instantiate
/// the `WrappedWalletStorage` with your own implementation of these functions for testing purposes,
/// or you can use one of the built in static versions of the `WrappedWalletStorage`.
struct WrappedWalletStorage {
/// properties that live on the `WalletStorageClient` type. Because of this, you can instantiate
/// the `WalletStorageClient` with your own implementation of these functions for testing purposes,
/// or you can use one of the built in static versions of the `WalletStorageClient`.
struct WalletStorageClient {
/// Store recovery phrase and optionally even birthday to the secured and persistent storage.
/// This function creates an instance of `StoredWallet` and automatically handles versioning of the stored data.
///
@ -36,12 +43,12 @@ struct WrappedWalletStorage {
/// - `WalletStorageError.uninitializedWallet`: when no wallet's data is found in the keychain.
/// - `WalletStorageError.storageError` when some unrecognized error occured.
/// - `WalletStorageError.unsupportedVersion` when wallet's version stored in the keychain is outdated.
let exportWallet: () throws -> StoredWallet
var exportWallet: () throws -> StoredWallet
/// Check if the wallet representation `StoredWallet` is present in the persistent storage.
///
/// - Returns: the information wheather some wallet is stored or is not available
let areKeysPresent: () throws -> Bool
var areKeysPresent: () throws -> Bool
/// Update the birthday in the securely stored wallet.
///
@ -65,52 +72,3 @@ struct WrappedWalletStorage {
/// There's no fate but what we make for ourselves - Sarah Connor.
let nukeWallet: () -> Void
}
extension WrappedWalletStorage {
public static func live(walletStorage: WalletStorage = WalletStorage(secItem: .live)) -> Self {
Self(
importWallet: { bip39, birthday, language, hasUserPassedPhraseBackupTest in
try walletStorage.importWallet(
bip39: bip39,
birthday: birthday,
language: language,
hasUserPassedPhraseBackupTest: hasUserPassedPhraseBackupTest
)
},
exportWallet: {
try walletStorage.exportWallet()
},
areKeysPresent: {
try walletStorage.areKeysPresent()
},
updateBirthday: { birthday in
try walletStorage.updateBirthday(birthday)
},
markUserPassedPhraseBackupTest: {
try walletStorage.markUserPassedPhraseBackupTest()
},
nukeWallet: {
walletStorage.nukeWallet()
}
)
}
public static let throwing = WrappedWalletStorage(
importWallet: { _, _, _, _ in
throw WalletStorage.WalletStorageError.alreadyImported
},
exportWallet: {
throw WalletStorage.WalletStorageError.uninitializedWallet
},
areKeysPresent: {
throw WalletStorage.WalletStorageError.uninitializedWallet
},
updateBirthday: { _ in
throw WalletStorage.KeychainError.encoding
},
markUserPassedPhraseBackupTest: {
throw WalletStorage.KeychainError.encoding
},
nukeWallet: { }
)
}

View File

@ -0,0 +1,43 @@
//
// WalletStorageLiveKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 15.11.2022.
//
import Foundation
import MnemonicSwift
import ZcashLightClientKit
import ComposableArchitecture
extension WalletStorageClient: DependencyKey {
static let liveValue = WalletStorageClient.live()
static func live(walletStorage: WalletStorage = WalletStorage(secItem: .live)) -> Self {
Self(
importWallet: { bip39, birthday, language, hasUserPassedPhraseBackupTest in
try walletStorage.importWallet(
bip39: bip39,
birthday: birthday,
language: language,
hasUserPassedPhraseBackupTest: hasUserPassedPhraseBackupTest
)
},
exportWallet: {
try walletStorage.exportWallet()
},
areKeysPresent: {
try walletStorage.areKeysPresent()
},
updateBirthday: { birthday in
try walletStorage.updateBirthday(birthday)
},
markUserPassedPhraseBackupTest: {
try walletStorage.markUserPassedPhraseBackupTest()
},
nukeWallet: {
walletStorage.nukeWallet()
}
)
}
}

View File

@ -0,0 +1,31 @@
//
// WalletStorageTestKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 14.11.2022.
//
import ComposableArchitecture
import XCTestDynamicOverlay
extension WalletStorageClient: TestDependencyKey {
static let testValue = Self(
importWallet: XCTUnimplemented("\(Self.self).importWallet"),
exportWallet: XCTUnimplemented("\(Self.self).exportWallet", placeholder: .placeholder),
areKeysPresent: XCTUnimplemented("\(Self.self).areKeysPresent", placeholder: false),
updateBirthday: XCTUnimplemented("\(Self.self).updateBirthday"),
markUserPassedPhraseBackupTest: XCTUnimplemented("\(Self.self).markUserPassedPhraseBackupTest"),
nukeWallet: XCTUnimplemented("\(Self.self).nukeWallet")
)
}
extension WalletStorageClient {
static let noOp = Self(
importWallet: { _, _, _, _ in },
exportWallet: { .placeholder },
areKeysPresent: { false },
updateBirthday: { _ in },
markUserPassedPhraseBackupTest: { },
nukeWallet: { }
)
}

View File

@ -1,20 +0,0 @@
//
// WalletStorageKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 03.11.2022.
//
import ComposableArchitecture
private enum WalletStorageKey: DependencyKey {
static let liveValue = WrappedWalletStorage.live()
static let testValue = WrappedWalletStorage.throwing
}
extension DependencyValues {
var walletStorage: WrappedWalletStorage {
get { self[WalletStorageKey.self] }
set { self[WalletStorageKey.self] = newValue }
}
}

View File

@ -1,95 +0,0 @@
//
// ZCashSDKEnvironment.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.04.2022.
//
import Foundation
import ZcashLightClientKit
import ComposableArchitecture
// swiftlint:disable:next private_over_fileprivate strict_fileprivate
fileprivate enum ZcashSDKConstants {
static let defaultBlockHeight = 1_629_724
static let endpointMainnetAddress = "lightwalletd.electriccoin.co"
static let endpointTestnetAddress = "lightwalletd.testnet.electriccoin.co"
static let endpointPort = 9067
static let mnemonicWordsMaxCount = 24
static let requiredTransactionConfirmations = 10
static let streamingCallTimeoutInMillis = Int64(10 * 60 * 60 * 1000) // ten hours
}
struct ZCashSDKEnvironment {
let defaultBirthday: BlockHeight
let endpoint: LightWalletEndpoint
let isMainnet: () -> Bool
let lightWalletService: LightWalletService
let memoCharLimit: Int
let mnemonicWordsMaxCount: Int
let network: ZcashNetwork
let requiredTransactionConfirmations: Int
let sdkVersion: String
}
extension ZCashSDKEnvironment {
static let mainnet = ZCashSDKEnvironment(
defaultBirthday: BlockHeight(ZcashSDKConstants.defaultBlockHeight),
endpoint: LightWalletEndpoint(
address: ZcashSDKConstants.endpointMainnetAddress,
port: ZcashSDKConstants.endpointPort,
secure: true,
streamingCallTimeoutInMillis: ZcashSDKConstants.streamingCallTimeoutInMillis
),
isMainnet: { true },
lightWalletService: LightWalletGRPCService(
endpoint: LightWalletEndpoint(
address: ZcashSDKConstants.endpointMainnetAddress,
port: ZcashSDKConstants.endpointPort,
secure: true,
streamingCallTimeoutInMillis: ZcashSDKConstants.streamingCallTimeoutInMillis
)
),
memoCharLimit: 512,
mnemonicWordsMaxCount: ZcashSDKConstants.mnemonicWordsMaxCount,
network: ZcashNetworkBuilder.network(for: .mainnet),
requiredTransactionConfirmations: ZcashSDKConstants.requiredTransactionConfirmations,
sdkVersion: "0.16.5-beta"
)
static let testnet = ZCashSDKEnvironment(
defaultBirthday: BlockHeight(ZcashSDKConstants.defaultBlockHeight),
endpoint: LightWalletEndpoint(
address: ZcashSDKConstants.endpointTestnetAddress,
port: ZcashSDKConstants.endpointPort,
secure: true,
streamingCallTimeoutInMillis: ZcashSDKConstants.streamingCallTimeoutInMillis
),
isMainnet: { false },
lightWalletService: LightWalletGRPCService(
endpoint: LightWalletEndpoint(
address: ZcashSDKConstants.endpointTestnetAddress,
port: ZcashSDKConstants.endpointPort,
secure: true,
streamingCallTimeoutInMillis: ZcashSDKConstants.streamingCallTimeoutInMillis
)
),
memoCharLimit: 512,
mnemonicWordsMaxCount: ZcashSDKConstants.mnemonicWordsMaxCount,
network: ZcashNetworkBuilder.network(for: .testnet),
requiredTransactionConfirmations: ZcashSDKConstants.requiredTransactionConfirmations,
sdkVersion: "0.16.5-beta"
)
}
private enum ZCashSDKEnvironmentKey: DependencyKey {
static let liveValue = ZCashSDKEnvironment.mainnet
static let testValue = ZCashSDKEnvironment.testnet
}
extension DependencyValues {
var zcashSDKEnvironment: ZCashSDKEnvironment {
get { self[ZCashSDKEnvironmentKey.self] }
set { self[ZCashSDKEnvironmentKey.self] = newValue }
}
}

View File

@ -0,0 +1,40 @@
//
// ZcashSDKEnvironmentInterface.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
import ZcashLightClientKit
extension DependencyValues {
var zcashSDKEnvironment: ZcashSDKEnvironment {
get { self[ZcashSDKEnvironment.self] }
set { self[ZcashSDKEnvironment.self] = newValue }
}
}
extension ZcashSDKEnvironment {
enum ZcashSDKConstants {
static let defaultBlockHeight = 1_629_724
static let endpointMainnetAddress = "lightwalletd.electriccoin.co"
static let endpointTestnetAddress = "lightwalletd.testnet.electriccoin.co"
static let endpointPort = 9067
static let mnemonicWordsMaxCount = 24
static let requiredTransactionConfirmations = 10
static let streamingCallTimeoutInMillis = Int64(10 * 60 * 60 * 1000) // ten hours
}
}
struct ZcashSDKEnvironment {
let defaultBirthday: BlockHeight
let endpoint: LightWalletEndpoint
let isMainnet: () -> Bool
let lightWalletService: LightWalletService
let memoCharLimit: Int
let mnemonicWordsMaxCount: Int
let network: ZcashNetwork
let requiredTransactionConfirmations: Int
let sdkVersion: String
}

View File

@ -0,0 +1,37 @@
//
// ZcashSDKEnvironmentLiveKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
import ZcashLightClientKit
extension ZcashSDKEnvironment: DependencyKey {
static let mainnet = ZcashSDKEnvironment.liveValue
static let liveValue = Self(
defaultBirthday: BlockHeight(ZcashSDKConstants.defaultBlockHeight),
endpoint: LightWalletEndpoint(
address: ZcashSDKConstants.endpointMainnetAddress,
port: ZcashSDKConstants.endpointPort,
secure: true,
streamingCallTimeoutInMillis: ZcashSDKConstants.streamingCallTimeoutInMillis
),
isMainnet: { true },
lightWalletService: LightWalletGRPCService(
endpoint: LightWalletEndpoint(
address: ZcashSDKConstants.endpointMainnetAddress,
port: ZcashSDKConstants.endpointPort,
secure: true,
streamingCallTimeoutInMillis: ZcashSDKConstants.streamingCallTimeoutInMillis
)
),
memoCharLimit: 512,
mnemonicWordsMaxCount: ZcashSDKConstants.mnemonicWordsMaxCount,
network: ZcashNetworkBuilder.network(for: .mainnet),
requiredTransactionConfirmations: ZcashSDKConstants.requiredTransactionConfirmations,
sdkVersion: "0.16.5-beta"
)
}

View File

@ -0,0 +1,38 @@
//
// ZcashSDKEnvironmentTestKey.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.11.2022.
//
import ComposableArchitecture
import ZcashLightClientKit
import XCTestDynamicOverlay
extension ZcashSDKEnvironment: TestDependencyKey {
static let testnet = ZcashSDKEnvironment.liveValue
static let testValue = Self(
defaultBirthday: BlockHeight(ZcashSDKConstants.defaultBlockHeight),
endpoint: LightWalletEndpoint(
address: ZcashSDKConstants.endpointTestnetAddress,
port: ZcashSDKConstants.endpointPort,
secure: true,
streamingCallTimeoutInMillis: ZcashSDKConstants.streamingCallTimeoutInMillis
),
isMainnet: { false },
lightWalletService: LightWalletGRPCService(
endpoint: LightWalletEndpoint(
address: ZcashSDKConstants.endpointTestnetAddress,
port: ZcashSDKConstants.endpointPort,
secure: true,
streamingCallTimeoutInMillis: ZcashSDKConstants.streamingCallTimeoutInMillis
)
),
memoCharLimit: 512,
mnemonicWordsMaxCount: ZcashSDKConstants.mnemonicWordsMaxCount,
network: ZcashNetworkBuilder.network(for: .testnet),
requiredTransactionConfirmations: ZcashSDKConstants.requiredTransactionConfirmations,
sdkVersion: "0.16.5-beta"
)
}

View File

@ -65,7 +65,7 @@ struct AppReducer: ReducerProtocol {
@Dependency(\.derivationTool) var derivationTool
@Dependency(\.mainQueue) var mainQueue
@Dependency(\.mnemonic) var mnemonic
@Dependency(\.randomPhrase) var randomPhrase
@Dependency(\.randomRecoveryPhrase) var randomRecoveryPhrase
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
@Dependency(\.walletStorage) var walletStorage
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
@ -105,7 +105,7 @@ struct AppReducer: ReducerProtocol {
case .phraseValidation(.proceedToHome):
state.route = .home
case .phraseValidation(.displayBackedUpPhrase), .phraseDisplay(.createPhrase):
case .phraseValidation(.displayBackedUpPhrase):
state.route = .phraseDisplay
case .phraseDisplay(.finishedPressed):
@ -120,7 +120,7 @@ struct AppReducer: ReducerProtocol {
case .deeplink(let url):
// get the latest synchronizer state
var synchronizerStatus = WrappedSDKSynchronizerState.unknown
var synchronizerStatus = SDKSynchronizerState.unknown
_ = sdkSynchronizer.stateChanged.sink { synchronizerStatus = $0 }
// process the deeplink only if app is initialized and synchronizer synced
@ -261,7 +261,7 @@ struct AppReducer: ReducerProtocol {
let recoveryPhrase = RecoveryPhrase(words: phraseWords)
state.phraseDisplayState.phrase = recoveryPhrase
state.phraseValidationState = randomPhrase.random(recoveryPhrase)
state.phraseValidationState = randomRecoveryPhrase.random(recoveryPhrase)
landingRoute = .phraseDisplay
} catch {
// TODO [#201]: - merge with issue 201 (https://github.com/zcash/secant-ios-wallet/issues/201) and its Error States
@ -286,10 +286,10 @@ struct AppReducer: ReducerProtocol {
try walletStorage.importWallet(newRandomPhrase, birthday, .english, false)
// start the backup phrase validation test
let randomPhraseWords = try mnemonic.asWords(newRandomPhrase)
let recoveryPhrase = RecoveryPhrase(words: randomPhraseWords)
let randomRecoveryPhraseWords = try mnemonic.asWords(newRandomPhrase)
let recoveryPhrase = RecoveryPhrase(words: randomRecoveryPhraseWords)
state.phraseDisplayState.phrase = recoveryPhrase
state.phraseValidationState = randomPhrase.random(recoveryPhrase)
state.phraseValidationState = randomRecoveryPhrase.random(recoveryPhrase)
return .concatenate(
Effect(value: .initializeSDK),
@ -341,8 +341,8 @@ struct AppReducer: ReducerProtocol {
extension AppReducer {
static func walletInitializationState(
databaseFiles: DatabaseFilesClient,
walletStorage: WrappedWalletStorage,
zcashSDKEnvironment: ZCashSDKEnvironment
walletStorage: WalletStorageClient,
zcashSDKEnvironment: ZcashSDKEnvironment
) -> InitializationState {
var keysPresent = false
do {
@ -388,8 +388,8 @@ extension AppReducer {
birthday: BlockHeight,
databaseFiles: DatabaseFilesClient,
derivationTool: DerivationToolClient,
mnemonic: WrappedMnemonic,
zcashSDKEnvironment: ZCashSDKEnvironment
mnemonic: MnemonicClient,
zcashSDKEnvironment: ZcashSDKEnvironment
) throws -> Initializer {
do {
let seedBytes = try mnemonic.toSeed(seedPhrase)

View File

@ -28,7 +28,7 @@ struct BalanceBreakdownReducer: ReducerProtocol {
enum Action: Equatable {
case onAppear
case onDisappear
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case synchronizerStateChanged(SDKSynchronizerState)
case updateLatestBlock
case updateSynchronizerStatus
}

View File

@ -64,7 +64,7 @@ struct HomeReducer: ReducerProtocol {
case request(RequestReducer.Action)
case send(SendFlowReducer.Action)
case scan(ScanReducer.Action)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case synchronizerStateChanged(SDKSynchronizerState)
case walletEvents(WalletEventsFlowReducer.Action)
case updateDrawer(DrawerOverlay)
case updateRoute(HomeReducer.State.Route?)

View File

@ -17,22 +17,15 @@ struct RecoveryPhraseDisplayReducer: ReducerProtocol {
}
enum Action: Equatable {
case createPhrase
case copyToBufferPressed
case finishedPressed
case phraseResponse(RecoveryPhrase)
}
@Dependency(\.newRecoveryPhrase) var newRecoveryPhrase
@Dependency(\.pasteboard) var pasteboard
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case .createPhrase:
return .run { send in
await send(.phraseResponse(newRecoveryPhrase()))
}
case .copyToBufferPressed:
guard let phrase = state.phrase?.toString() else { return .none }
pasteboard.setString(phrase)

View File

@ -53,13 +53,13 @@ struct RecoveryPhraseValidationFlowReducer: ReducerProtocol {
@Dependency(\.feedbackGenerator) var feedbackGenerator
@Dependency(\.mainQueue) var mainQueue
@Dependency(\.pasteboard) var pasteboard
@Dependency(\.randomPhrase) var randomPhrase
@Dependency(\.randomRecoveryPhrase) var randomRecoveryPhrase
// swiftlint:disable:next cyclomatic_complexity
func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask<Action> {
switch action {
case .reset:
state = randomPhrase.random(state.phrase)
state = randomRecoveryPhrase.random(state.phrase)
state.route = .validation
// FIXME [#186]: Resetting causes route to be nil = preamble screen, hence setting the .validation. The transition back is not animated
// though
@ -101,7 +101,7 @@ struct RecoveryPhraseValidationFlowReducer: ReducerProtocol {
case .updateRoute(let route):
guard let route = route else {
state = randomPhrase.random(state.phrase)
state = randomRecoveryPhrase.random(state.phrase)
return .none
}
state.route = route

View File

@ -80,7 +80,7 @@ struct SendFlowReducer: ReducerProtocol {
case onDisappear
case sendConfirmationPressed
case sendTransactionResult(Result<TransactionState, NSError>)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case synchronizerStateChanged(SDKSynchronizerState)
case transactionAddressInput(TransactionAddressTextFieldReducer.Action)
case transactionAmountInput(TransactionAmountTextFieldReducer.Action)
case updateRoute(SendFlowReducer.State.Route?)

View File

@ -33,7 +33,7 @@ struct WalletEventsFlowReducer: ReducerProtocol {
case openBlockExplorer(URL?)
case updateRoute(WalletEventsFlowReducer.State.Route?)
case replyTo(String)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case synchronizerStateChanged(SDKSynchronizerState)
case updateWalletEvents([WalletEvent])
case warnBeforeLeavingApp(URL?)
}

View File

@ -18,3 +18,13 @@ struct StoredWallet: Codable, Equatable {
var birthday: BlockHeight?
var hasUserPassedPhraseBackupTest: Bool
}
extension StoredWallet {
static let placeholder = Self(
language: .english,
seedPhrase: "",
version: 0,
birthday: 0,
hasUserPassedPhraseBackupTest: false
)
}

View File

@ -1,29 +0,0 @@
//
// WrappedFeedbackGenerator.swift
// secant-testnet
//
// Created by Lukáš Korba on 12.05.2022.
//
import Foundation
import UIKit
struct WrappedFeedbackGenerator {
let generateSuccessFeedback: () -> Void
let generateWarningFeedback: () -> Void
let generateErrorFeedback: () -> Void
}
extension WrappedFeedbackGenerator {
static let haptic = WrappedFeedbackGenerator(
generateSuccessFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.success) },
generateWarningFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.warning) },
generateErrorFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.error) }
)
static let silent = WrappedFeedbackGenerator(
generateSuccessFeedback: { },
generateWarningFeedback: { },
generateErrorFeedback: { }
)
}

View File

@ -1,22 +0,0 @@
//
// WrappedNotificationCenter.swift
// secant-testnet
//
// Created by Lukáš Korba on 04.05.2022.
//
import Foundation
struct WrappedNotificationCenter {
let publisherFor: (Notification.Name) -> NotificationCenter.Publisher?
}
extension WrappedNotificationCenter {
static let live = WrappedNotificationCenter(
publisherFor: { NotificationCenter.default.publisher(for: $0) }
)
static let mock = WrappedNotificationCenter(
publisherFor: { _ in nil }
)
}

View File

@ -1,31 +0,0 @@
//
// WrappedPasteboard.swift
// secant-testnet
//
// Created by Lukáš Korba on 12.05.2022.
//
import Foundation
import UIKit
struct WrappedPasteboard {
let setString: (String) -> Void
let getString: () -> String?
}
extension WrappedPasteboard {
private struct TestPasteboard {
static var general = TestPasteboard()
var string: String?
}
static let live = WrappedPasteboard(
setString: { UIPasteboard.general.string = $0 },
getString: { UIPasteboard.general.string }
)
static let test = WrappedPasteboard(
setString: { TestPasteboard.general.string = $0 },
getString: { TestPasteboard.general.string }
)
}

View File

@ -1,18 +0,0 @@
//
// WrappedRecoveryPhraseRandomizer.swift
// secant-testnet
//
// Created by Lukáš Korba on 01.06.2022.
//
import Foundation
struct WrappedRecoveryPhraseRandomizer {
let random: (RecoveryPhrase) -> RecoveryPhraseValidationFlowReducer.State
}
extension WrappedRecoveryPhraseRandomizer {
static let live = WrappedRecoveryPhraseRandomizer(
random: { RecoveryPhraseRandomizer().random(phrase: $0) }
)
}

View File

@ -1,533 +0,0 @@
//
// WrappedSDKSynchronizer.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.04.2022.
//
import Foundation
import ZcashLightClientKit
import Combine
import ComposableArchitecture
enum WrappedSDKSynchronizerState: Equatable {
case unknown
case transactionsUpdated
case started
case progressUpdated
case statusWillUpdate
case synced
case stopped
case disconnected
case syncing
case downloading
case validating
case scanning
case enhancing
case fetching
case minedTransaction
case foundTransactions
case failed
case connectionStateChanged
}
protocol WrappedSDKSynchronizer {
var blockProcessor: CompactBlockProcessor? { get }
var notificationCenter: WrappedNotificationCenter { get }
var synchronizer: SDKSynchronizer? { get }
var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never> { get }
var walletBirthday: BlockHeight? { get }
var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState? { get }
func prepareWith(initializer: Initializer) throws
func start(retry: Bool) throws
func stop()
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?)
func statusSnapshot() -> SyncStatusSnapshot
func rewind(_ policy: RewindPolicy) throws
func getShieldedBalance() -> WalletBalance?
func getTransparentBalance() -> WalletBalance?
func getAllClearedTransactions() -> Effect<[WalletEvent], Never>
func getAllPendingTransactions() -> Effect<[WalletEvent], Never>
func getAllTransactions() -> Effect<[WalletEvent], Never>
func getTransparentAddress(account: Int) -> TransparentAddress?
func getShieldedAddress(account: Int) -> SaplingShieldedAddress?
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never>
}
extension WrappedSDKSynchronizer {
func start() throws {
try start(retry: false)
}
func getTransparentAddress() -> TransparentAddress? {
getTransparentAddress(account: 0)
}
func getShieldedAddress() -> SaplingShieldedAddress? {
getShieldedAddress(account: 0)
}
}
class LiveWrappedSDKSynchronizer: WrappedSDKSynchronizer {
private var cancellables: [AnyCancellable] = []
private(set) var blockProcessor: CompactBlockProcessor?
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never>
private(set) var notificationCenter: WrappedNotificationCenter
private(set) var walletBirthday: BlockHeight?
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
init(notificationCenter: WrappedNotificationCenter = .live) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(.unknown)
}
deinit {
synchronizer?.stop()
}
func prepareWith(initializer: Initializer) throws {
synchronizer = try SDKSynchronizer(initializer: initializer)
notificationCenter.publisherFor(.synchronizerStarted)?
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.synchronizerStarted() }
.store(in: &cancellables)
notificationCenter.publisherFor(.synchronizerSynced)?
.receive(on: DispatchQueue.main)
.sink { [weak self] output in
let synchronizerState = output.userInfo?[SDKSynchronizer.NotificationKeys.synchronizerState] as? SDKSynchronizer.SynchronizerState
self?.synchronizerSynced(synchronizerState)
}
.store(in: &cancellables)
notificationCenter.publisherFor(.synchronizerProgressUpdated)?
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.synchronizerProgressUpdated() }
.store(in: &cancellables)
notificationCenter.publisherFor(.synchronizerStopped)?
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.synchronizerStopped() }
.store(in: &cancellables)
try synchronizer?.prepare()
blockProcessor = CompactBlockProcessor(initializer: initializer)
walletBirthday = initializer.walletBirthday
}
func start(retry: Bool) throws {
try synchronizer?.start(retry: retry)
}
func stop() {
synchronizer?.stop()
}
func synchronizerStarted() {
stateChanged.send(.started)
}
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) {
stateChanged.send(.synced)
latestScannedSynchronizerState = synchronizerState
}
func synchronizerProgressUpdated() {
stateChanged.send(.progressUpdated)
}
func synchronizerStopped() {
stateChanged.send(.stopped)
}
func statusSnapshot() -> SyncStatusSnapshot {
guard let synchronizer = synchronizer else {
return .default
}
return SyncStatusSnapshot.snapshotFor(state: synchronizer.status)
}
func rewind(_ policy: RewindPolicy) throws {
stop()
var height: BlockHeight?
switch policy {
case .quick, .transaction:
break
case .birthday:
height = walletBirthday
case .height(let blockheight):
height = blockheight
}
do {
_ = try blockProcessor?.rewindTo(height)
} catch {
throw SynchronizerError.rewindError(underlyingError: error)
}
}
func getShieldedBalance() -> WalletBalance? {
latestScannedSynchronizerState?.shieldedBalance
}
func getTransparentBalance() -> WalletBalance? {
latestScannedSynchronizerState?.transparentBalance
}
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
if let clearedTransactions = try? synchronizer?.allClearedTransactions() {
return Effect(value: clearedTransactions.map {
let transaction = TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil))
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
})
}
return .none
}
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
if let pendingTransactions = try? synchronizer?.allPendingTransactions(),
let syncedBlockHeight = synchronizer?.latestScannedHeight {
return Effect(value: pendingTransactions.map {
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
})
}
return .none
}
func getAllTransactions() -> Effect<[WalletEvent], Never> {
if let pendingTransactions = try? synchronizer?.allPendingTransactions(),
let clearedTransactions = try? synchronizer?.allClearedTransactions(),
let syncedBlockHeight = synchronizer?.latestScannedHeight {
let clearedTxs: [WalletEvent] = clearedTransactions.map {
let transaction = TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil))
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
let pendingTxs: [WalletEvent] = pendingTransactions.map {
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
let txs = clearedTxs.filter { cleared in
pendingTxs.first { pending in
pending.id == cleared.id
} == nil
}
return .merge(
Effect(value: txs),
Effect(value: pendingTxs)
)
.flatMap(Publishers.Sequence.init(sequence:))
.collect()
.eraseToEffect()
}
return .none
}
func getTransparentAddress(account: Int) -> TransparentAddress? {
synchronizer?.getTransparentAddress(accountIndex: account)
}
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? {
synchronizer?.getShieldedAddress(accountIndex: account)
}
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never> {
Deferred {
Future { [weak self] promise in
self?.synchronizer?.sendToAddress(
spendingKey: spendingKey,
zatoshi: zatoshi,
toAddress: recipientAddress,
memo: memo,
from: account) { result in
switch result {
case .failure(let error as NSError):
promise(.failure(error))
case .success(let pendingTx):
promise(.success(TransactionState(pendingTransaction: pendingTx)))
}
}
}
}
.mapError { $0 as NSError }
.catchToEffect()
}
}
class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
private var cancellables: [AnyCancellable] = []
private(set) var blockProcessor: CompactBlockProcessor?
private(set) var notificationCenter: WrappedNotificationCenter
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never>
private(set) var walletBirthday: BlockHeight?
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
init(notificationCenter: WrappedNotificationCenter = .mock) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(.unknown)
}
deinit {
synchronizer?.stop()
}
func prepareWith(initializer: Initializer) throws {
try synchronizer?.prepare()
}
func start(retry: Bool) throws {
try synchronizer?.start(retry: retry)
}
func stop() {
synchronizer?.stop()
}
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) {
stateChanged.send(.synced)
}
func statusSnapshot() -> SyncStatusSnapshot {
guard let synchronizer = synchronizer else {
return .default
}
return SyncStatusSnapshot.snapshotFor(state: synchronizer.status)
}
func rewind(_ policy: RewindPolicy) throws { }
func getShieldedBalance() -> WalletBalance? {
WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000))
}
func getTransparentBalance() -> WalletBalance? {
WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000))
}
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "1"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "2"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "3"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "4"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "5")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039606, amount: Zatoshi(6), status: .paid(success: false)),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7)),
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true)),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9))
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date
)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllTransactions() -> Effect<[WalletEvent], Never> {
return .merge(
getAllClearedTransactions(),
getAllPendingTransactions()
)
.flatMap(Publishers.Sequence.init(sequence:))
.collect()
.eraseToEffect()
}
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? { nil }
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never> {
let transactionState = TransactionState(
expirationHeight: 40,
memo: memo,
minedHeight: 50,
shielded: true,
zAddress: "tteafadlamnelkqe",
fee: Zatoshi(10),
id: "id",
status: .paid(success: true),
timestamp: 1234567,
zecAmount: Zatoshi(10)
)
return Effect(value: Result.success(transactionState))
}
}
class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
private(set) var blockProcessor: CompactBlockProcessor?
private(set) var notificationCenter: WrappedNotificationCenter
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never>
private(set) var walletBirthday: BlockHeight?
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
init(notificationCenter: WrappedNotificationCenter = .mock) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(.unknown)
}
func prepareWith(initializer: Initializer) throws { }
func start(retry: Bool) throws { }
func stop() { }
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) { }
func statusSnapshot() -> SyncStatusSnapshot { .default }
func rewind(_ policy: RewindPolicy) throws { }
func getShieldedBalance() -> WalletBalance? { nil }
func getTransparentBalance() -> WalletBalance? { nil }
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(
date: 1651039606,
amount: Zatoshi(6),
status: .paid(success: false),
uuid: "ff66"
),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.amount.amount > 5 ? .pending : $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllTransactions() -> Effect<[WalletEvent], Never> {
return .merge(
getAllClearedTransactions(),
getAllPendingTransactions()
)
.flatMap(Publishers.Sequence.init(sequence:))
.collect()
.eraseToEffect()
}
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? { "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8" }
func sendTransaction(
with spendingKey: String,
zatoshi: Zatoshi,
to recipientAddress: String,
memo: String?,
from account: Int
) -> Effect<Result<TransactionState, NSError>, Never> {
return Effect(value: Result.failure(SynchronizerError.criticalError as NSError))
}
func updateStateChanged(_ newState: WrappedSDKSynchronizerState) {
stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(newState)
}
}

View File

@ -1,40 +0,0 @@
//
// WrappedUserDefaults.swift
// secant-testnet
//
// Created by Lukáš Korba on 03.05.2022.
//
import Foundation
struct WrappedUserDefaults {
let objectForKey: @Sendable (String) -> Any?
let remove: @Sendable (String) async -> Void
let setValue: @Sendable (Any?, String) async -> Void
let synchronize: @Sendable () async -> Bool
}
/// `UserDefaults` is thread-safe class. Because of that we can mark it as `Sendable` on our own. If it's marked as `Sendable` in `Foundation` in
/// future we can simply remove this line and be ok. This is probably simpliest and easiest way how to fix warnings about `UserDefaults` not being
/// sendable.
extension UserDefaults: @unchecked Sendable { }
extension WrappedUserDefaults {
static func live(
userDefaults: UserDefaults = .standard
) -> Self {
Self(
objectForKey: { userDefaults.object(forKey: $0) },
remove: { userDefaults.removeObject(forKey: $0) },
setValue: { userDefaults.set($0, forKey: $1) },
synchronize: { userDefaults.synchronize() }
)
}
static let mock = WrappedUserDefaults(
objectForKey: { _ in },
remove: { _ in },
setValue: { _, _ in },
synchronize: { true }
)
}

View File

@ -10,14 +10,6 @@ import XCTest
import ComposableArchitecture
class AppInitializationTests: XCTestCase {
var storage = WalletStorage(secItem: .live)
override func setUp() {
super.setUp()
storage.zcashStoredWalletPrefix = "test_app_"
storage.deleteData(forKey: WalletStorage.Constants.zcashStoredWallet)
}
/// This integration test starts with finishing the app launch and triggering bunch of initialization proceedures.
/// 1. The app calls .checkWalletInitialization delayed by 0.02 seconds to ensure keychain is successfuly operational.
/// 2. The .respondToWalletInitializationState is triggered to decide the state of the wallet.
@ -25,13 +17,10 @@ class AppInitializationTests: XCTestCase {
/// 4. The .checkBackupPhraseValidation is triggered to check the validation state.
/// 5. The user hasn't finished the backup phrase test so the display phrase is presented.
func testDidFinishLaunching_to_InitializedWallet() throws {
// the test needs to pass the exportWallet() so we simulate some in the keychain
try storage.importWallet(bip39: "one two three", birthday: nil)
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let recoveryPhrase = RecoveryPhrase(words: try WrappedMnemonic.mock.randomMnemonicWords())
let recoveryPhrase = RecoveryPhrase(words: try MnemonicClient.mock.randomMnemonicWords())
let phraseValidationState = RecoveryPhraseValidationFlowReducer.State(
phrase: recoveryPhrase,
@ -48,7 +37,7 @@ class AppInitializationTests: XCTestCase {
route: nil
)
let recoveryPhraseRandomizer = WrappedRecoveryPhraseRandomizer(
let recoveryPhraseRandomizer = RecoveryPhraseRandomizerClient(
random: { _ in
let missingIndices = [2, 0, 3, 5]
let missingWordChipKind = [
@ -81,18 +70,6 @@ class AppInitializationTests: XCTestCase {
}
)
let emptyURL = URL(fileURLWithPath: "")
let dbFiles = DatabaseFilesClient(
documentsDirectory: { emptyURL },
cacheDbURLFor: { _ in emptyURL },
dataDbURLFor: { _ in emptyURL },
outputParamsURLFor: { _ in emptyURL },
pendingDbURLFor: { _ in emptyURL },
spendParamsURLFor: { _ in emptyURL },
areDbFilesPresentFor: { _ in true },
nukeDbFilesFor: { _ in throw DatabaseFiles.DatabaseFilesError.nukeFiles }
)
let appState = AppReducer.State(
homeState: .placeholder,
onboardingState: .init(
@ -109,13 +86,16 @@ class AppInitializationTests: XCTestCase {
let store = TestStore(
initialState: appState,
reducer: AppReducer()
.dependency(\.databaseFiles, dbFiles)
.dependency(\.walletStorage, .live(walletStorage: storage))
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.randomPhrase, recoveryPhraseRandomizer)
)
store.dependencies.derivationTool = .noop
store.dependencies.databaseFiles = .noOp
store.dependencies.databaseFiles.areDbFilesPresentFor = { _ in true }
store.dependencies.derivationTool = .noOp
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
store.dependencies.mnemonic = .mock
store.dependencies.randomRecoveryPhrase = recoveryPhraseRandomizer
store.dependencies.walletStorage.exportWallet = { .placeholder }
store.dependencies.walletStorage.areKeysPresent = { true }
// Root of the test, the app finished the launch process and triggers the checks and initializations.
store.send(.appDelegate(.didFinishLaunching))
@ -133,8 +113,9 @@ class AppInitializationTests: XCTestCase {
store.receive(.initializeSDK) { state in
state.storedWallet = StoredWallet(
language: .english,
seedPhrase: "one two three",
version: 1,
seedPhrase: "",
version: 0,
birthday: 0,
hasUserPassedPhraseBackupTest: false
)
}
@ -160,26 +141,16 @@ class AppInitializationTests: XCTestCase {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let emptyURL = URL(fileURLWithPath: "")
let dbFiles = DatabaseFilesClient(
documentsDirectory: { emptyURL },
cacheDbURLFor: { _ in emptyURL },
dataDbURLFor: { _ in emptyURL },
outputParamsURLFor: { _ in emptyURL },
pendingDbURLFor: { _ in emptyURL },
spendParamsURLFor: { _ in emptyURL },
areDbFilesPresentFor: { _ in true },
nukeDbFilesFor: { _ in throw DatabaseFiles.DatabaseFilesError.nukeFiles }
)
let store = TestStore(
initialState: .placeholder,
reducer: AppReducer()
.dependency(\.databaseFiles, dbFiles)
.dependency(\.walletStorage, .live(walletStorage: storage))
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
)
store.dependencies.databaseFiles = .noOp
store.dependencies.databaseFiles.areDbFilesPresentFor = { _ in true }
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
store.dependencies.walletStorage = .noOp
// Root of the test, the app finished the launch process and triggers the checks and initializations.
store.send(.appDelegate(.didFinishLaunching))
@ -206,12 +177,12 @@ class AppInitializationTests: XCTestCase {
let store = TestStore(
initialState: .placeholder,
reducer: AppReducer()
.dependency(\.walletStorage, .live(walletStorage: storage))
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
)
store.dependencies.databaseFiles = .throwing
store.dependencies.databaseFiles = .noOp
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
store.dependencies.walletStorage = .noOp
// Root of the test, the app finished the launch process and triggers the checks and initializations.
store.send(.appDelegate(.didFinishLaunching))

View File

@ -14,8 +14,8 @@ class AppTests: XCTestCase {
func testWalletInitializationState_Uninitialized() throws {
let walletState = AppReducer.walletInitializationState(
databaseFiles: .throwing,
walletStorage: .throwing,
databaseFiles: .noOp,
walletStorage: .noOp,
zcashSDKEnvironment: .testnet
)
@ -23,15 +23,15 @@ class AppTests: XCTestCase {
}
func testWalletInitializationState_FilesPresentKeysMissing() throws {
let wfmMock = WrappedFileManager(
url: { _, _, _, _ in URL(fileURLWithPath: "") },
let wfmMock = FileManagerClient(
url: { _, _, _, _ in .emptyURL },
fileExists: { _ in return true },
removeItem: { _ in }
)
let walletState = AppReducer.walletInitializationState(
databaseFiles: .live(databaseFiles: DatabaseFiles(fileManager: wfmMock)),
walletStorage: .throwing,
walletStorage: .noOp,
zcashSDKEnvironment: .testnet
)
@ -39,15 +39,15 @@ class AppTests: XCTestCase {
}
func testWalletInitializationState_FilesMissingKeysMissing() throws {
let wfmMock = WrappedFileManager(
url: { _, _, _, _ in URL(fileURLWithPath: "") },
let wfmMock = FileManagerClient(
url: { _, _, _, _ in .emptyURL },
fileExists: { _ in return false },
removeItem: { _ in }
)
let walletState = AppReducer.walletInitializationState(
databaseFiles: .live(databaseFiles: DatabaseFiles(fileManager: wfmMock)),
walletStorage: .throwing,
walletStorage: .noOp,
zcashSDKEnvironment: .testnet
)
@ -62,10 +62,10 @@ class AppTests: XCTestCase {
let store = TestStore(
initialState: .placeholder,
reducer: AppReducer()
.dependency(\.mainQueue, Self.testScheduler.eraseToAnyScheduler())
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.mainQueue = Self.testScheduler.eraseToAnyScheduler()
store.send(.respondToWalletInitializationState(.uninitialized))
Self.testScheduler.advance(by: 3)
@ -93,6 +93,9 @@ class AppTests: XCTestCase {
reducer: AppReducer()
)
store.dependencies.walletStorage = .noOp
store.dependencies.walletStorage.exportWallet = { throw "export failed" }
store.send(.respondToWalletInitializationState(.filesMissing)) { state in
state.appInitializationState = .filesMissing
}
@ -111,6 +114,9 @@ class AppTests: XCTestCase {
reducer: AppReducer()
)
store.dependencies.walletStorage = .noOp
store.dependencies.walletStorage.exportWallet = { throw "export failed" }
store.send(.respondToWalletInitializationState(.initialized))
store.receive(.initializeSDK) { state in

View File

@ -15,7 +15,9 @@ class RecoveryPhraseDisplayReducerTests: XCTestCase {
initialState: RecoveryPhraseDisplayStore.test,
reducer: RecoveryPhraseDisplayReducer()
)
store.dependencies.pasteboard = .testPasteboard
store.send(.copyToBufferPressed) {
$0.phrase = .placeholder
$0.showCopyToBufferAlert = true

View File

@ -12,19 +12,13 @@ import ZcashLightClientKit
class BalanceBreakdownTests: XCTestCase {
func testOnAppear() throws {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: .placeholder,
reducer: BalanceBreakdownReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
)
store.send(.onAppear)
testScheduler.advance(by: 0.1)
// expected side effects as a result of .onAppear registration
store.receive(.synchronizerStateChanged(.unknown))
store.receive(.updateSynchronizerStatus)

View File

@ -84,17 +84,16 @@ class DeeplinkTests: XCTestCase {
reducer: AppReducer()
)
let synchronizer = TestWrappedSDKSynchronizer()
synchronizer.updateStateChanged(.synced)
store.dependencies.sdkSynchronizer = synchronizer
guard let url = URL(string: "zcash:///home") else {
return XCTFail("Deeplink: 'testDeeplinkRequest_homeURL' URL is expected to be valid.")
}
store.dependencies.deeplink = DeeplinkClient(
resolveDeeplinkURL: { _, _ in Deeplink.Route.home }
)
let synchronizer = NoopSDKSynchronizer()
synchronizer.updateStateChanged(.synced)
store.dependencies.sdkSynchronizer = synchronizer
guard let url = URL(string: "zcash:///home") else {
return XCTFail("Deeplink: 'testDeeplinkRequest_homeURL' URL is expected to be valid.")
}
_ = await store.send(.deeplink(url))
@ -116,7 +115,7 @@ class DeeplinkTests: XCTestCase {
}
func testDeeplinkRequest_Received_Send() async throws {
let synchronizer = TestWrappedSDKSynchronizer()
let synchronizer = NoopSDKSynchronizer()
synchronizer.updateStateChanged(.synced)
var appState = AppReducer.State.placeholder
@ -126,7 +125,6 @@ class DeeplinkTests: XCTestCase {
let store = TestStore(
initialState: appState,
reducer: AppReducer()
.dependency(\.sdkSynchronizer, synchronizer)
)
guard let url = URL(string: "zcash:///home/send?address=address&memo=some%20text&amount=123000000") else {
@ -136,6 +134,7 @@ class DeeplinkTests: XCTestCase {
store.dependencies.deeplink = DeeplinkClient(
resolveDeeplinkURL: { _, _ in Deeplink.Route.send(amount: 123_000_000, address: "address", memo: "some text") }
)
store.dependencies.sdkSynchronizer = synchronizer
_ = await store.send(.deeplink(url))

View File

@ -32,9 +32,11 @@ class HomeTests: XCTestCase {
let store = TestStore(
initialState: .placeholder,
reducer: HomeReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
)
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
store.dependencies.sdkSynchronizer = SDKSynchronizerDependency.mock
store.send(.synchronizerStateChanged(.synced))
testScheduler.advance(by: 0.01)
@ -44,11 +46,11 @@ class HomeTests: XCTestCase {
// ad 2.
let transactionsHelper: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "1"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "2"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "3"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "4"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "5")
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
let walletEvents: [WalletEvent] = transactionsHelper.map {
let transaction = TransactionState.placeholder(

View File

@ -26,9 +26,11 @@ class ImportWalletTests: XCTestCase {
let store = TestStore(
initialState: .placeholder,
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
)
store.dependencies.mnemonic = .noOp
store.dependencies.mnemonic.isValid = { _ in throw "invalid mnemonic" }
store.send(.binding(.set(\.$importedSeedPhrase, "one two three"))) { state in
state.importedSeedPhrase = "one two three"
state.wordsCount = 3
@ -40,9 +42,11 @@ class ImportWalletTests: XCTestCase {
let store = TestStore(
initialState: ImportWalletReducer.State(maxWordsCount: 24),
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
)
store.dependencies.mnemonic = .noOp
store.dependencies.mnemonic.isValid = { _ in throw "invalid mnemonic" }
store.send(.binding(.set(\.$importedSeedPhrase, "a a a a a a a a a a a a a a a a a a a a a a a a"))) { state in
state.importedSeedPhrase = "a a a a a a a a a a a a a a a a a a a a a a a a"
state.wordsCount = 24
@ -55,9 +59,10 @@ class ImportWalletTests: XCTestCase {
let store = TestStore(
initialState: ImportWalletReducer.State(maxWordsCount: 24),
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
)
store.dependencies.mnemonic = .noOp
store.send(
.binding(
.set(
@ -142,9 +147,11 @@ class ImportWalletTests: XCTestCase {
let store = TestStore(
initialState: ImportWalletReducer.State(maxWordsCount: 24),
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
)
store.dependencies.mnemonic = .noOp
store.dependencies.mnemonic.isValid = { _ in throw "invalid mnemonic" }
store.send(.binding(.set(\.$birthdayHeight, "1700000"))) { state in
state.birthdayHeight = "1700000"
state.birthdayHeightValue = 1_700_000
@ -166,9 +173,11 @@ class ImportWalletTests: XCTestCase {
let store = TestStore(
initialState: ImportWalletReducer.State(maxWordsCount: 24),
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
)
store.dependencies.mnemonic = .noOp
store.dependencies.mnemonic.isValid = { _ in throw "invalid mnemonic" }
store.send(.binding(.set(\.$birthdayHeight, "1600000"))) { state in
state.birthdayHeight = "1600000"
}
@ -189,9 +198,10 @@ class ImportWalletTests: XCTestCase {
let store = TestStore(
initialState: ImportWalletReducer.State(maxWordsCount: 24),
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
)
store.dependencies.mnemonic = .noOp
store.send(.binding(.set(\.$birthdayHeight, "1600000"))) { state in
state.birthdayHeight = "1600000"
}
@ -229,9 +239,10 @@ class ImportWalletTests: XCTestCase {
let store = TestStore(
initialState: ImportWalletReducer.State(maxWordsCount: 24),
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
)
store.dependencies.mnemonic = .noOp
store.send(.binding(.set(\.$birthdayHeight, "1700000"))) { state in
state.birthdayHeight = "1700000"
state.birthdayHeightValue = 1_700_000
@ -270,9 +281,10 @@ class ImportWalletTests: XCTestCase {
let store = TestStore(
initialState: ImportWalletReducer.State(maxWordsCount: 24),
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
)
store.dependencies.mnemonic = .noOp
store.send(
.binding(
.set(
@ -303,10 +315,6 @@ class ImportWalletTests: XCTestCase {
}
func testRestoreWallet() throws {
var storage = WalletStorage(secItem: .live)
storage.zcashStoredWalletPrefix = "test_importWallet_"
storage.deleteData(forKey: WalletStorage.Constants.zcashStoredWallet)
let store = TestStore(
initialState: ImportWalletReducer.State(
alert: nil,
@ -324,10 +332,11 @@ class ImportWalletTests: XCTestCase {
birthdayHeightValue: 1_700_000
),
reducer: ImportWalletReducer()
.dependency(\.mnemonic, .live)
.dependency(\.walletStorage, .live(walletStorage: storage))
)
store.dependencies.mnemonic = .noOp
store.dependencies.walletStorage = .noOp
store.send(.restoreWallet) { state in
state.alert = AlertState(
title: TextState("Success"),

View File

@ -14,10 +14,10 @@ class ProfileTests: XCTestCase {
let store = TestStore(
initialState: .placeholder,
reducer: ProfileReducer()
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.appVersion = .mock
store.dependencies.sdkSynchronizer = SDKSynchronizerDependency.mock
store.send(.onAppear) { state in
state.address = "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8"

View File

@ -291,7 +291,6 @@ class RecoveryPhraseValidationTests: XCTestCase {
let store = TestStore(
initialState: currentStep,
reducer: RecoveryPhraseValidationFlowReducer()
.dependency(\.mainQueue, RecoveryPhraseValidationTests.testScheduler.eraseToAnyScheduler())
)
let expectedMissingWordChips = [
@ -308,6 +307,8 @@ class RecoveryPhraseValidationTests: XCTestCase {
ValidationWord(groupIndex: 3, word: "pizza")
]
store.dependencies.mainQueue = Self.testScheduler.eraseToAnyScheduler()
store.send(.move(wordChip: PhraseChip.Kind.unassigned(word: "pizza"), intoGroup: 3)) {
$0.missingWordChips = expectedMissingWordChips
$0.validationWords = expectedValidationWords
@ -362,7 +363,6 @@ class RecoveryPhraseValidationTests: XCTestCase {
let store = TestStore(
initialState: currentStep,
reducer: RecoveryPhraseValidationFlowReducer()
.dependency(\.mainQueue, RecoveryPhraseValidationTests.testScheduler.eraseToAnyScheduler())
)
let expectedMissingWordChips = [
@ -379,6 +379,9 @@ class RecoveryPhraseValidationTests: XCTestCase {
ValidationWord(groupIndex: 3, word: "pizza")
]
store.dependencies.feedbackGenerator = .noOp
store.dependencies.mainQueue = Self.testScheduler.eraseToAnyScheduler()
store.send(.move(wordChip: PhraseChip.Kind.unassigned(word: "pizza"), intoGroup: 3)) {
$0.missingWordChips = expectedMissingWordChips
$0.validationWords = expectedValidationWords

View File

@ -23,7 +23,7 @@ class ScanTests: XCTestCase {
reducer: ScanReducer()
)
store.dependencies.captureDevice = .noop
store.dependencies.captureDevice = .noOp
store.send(.onAppear) { state in
state.isTorchAvailable = false
@ -39,7 +39,7 @@ class ScanTests: XCTestCase {
reducer: ScanReducer()
)
store.dependencies.captureDevice = .noop
store.dependencies.captureDevice = .noOp
store.send(.torchPressed) { state in
state.isTorchOn = true
@ -54,7 +54,7 @@ class ScanTests: XCTestCase {
reducer: ScanReducer()
)
store.dependencies.captureDevice = .noop
store.dependencies.captureDevice = .noOp
store.send(.torchPressed) { state in
state.isTorchOn = false
@ -67,6 +67,8 @@ class ScanTests: XCTestCase {
reducer: ScanReducer()
)
store.dependencies.uriParser.isValidURI = { _ in false }
store.send(.scan("test")) { state in
state.scanStatus = .value("test")
state.isValidValue = false
@ -79,9 +81,11 @@ class ScanTests: XCTestCase {
let store = TestStore(
initialState: ScanReducer.State(),
reducer: ScanReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
)
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
store.dependencies.uriParser.isValidURI = { _ in true }
store.send(.scan("t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po")) { state in
state.scanStatus = .value("t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po")
state.isValidValue = true

View File

@ -28,22 +28,21 @@ class SendTests: XCTestCase {
}
func testSendSucceeded() throws {
// the test needs to pass the exportWallet() so we simulate some in the keychain
try storage.importWallet(bip39: "one two three", birthday: nil)
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: storage))
)
store.dependencies.derivationTool = .noop
store.dependencies.derivationTool = .noOp
store.dependencies.derivationTool.deriveSpendingKeys = { _, _ in [""] }
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
store.dependencies.mnemonic = .mock
store.dependencies.sdkSynchronizer = SDKSynchronizerDependency.mock
store.dependencies.walletStorage.exportWallet = { .placeholder }
// simulate the sending confirmation button to be pressed
store.send(.sendConfirmationPressed) { state in
// once sending is confirmed, the attemts to try to send again by pressing the button
@ -85,9 +84,6 @@ class SendTests: XCTestCase {
}
func testSendSucceededWithoutMemo() throws {
// the test needs to pass the exportWallet() so we simulate some in the keychain
try storage.importWallet(bip39: "one two three", birthday: nil)
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
@ -97,12 +93,14 @@ class SendTests: XCTestCase {
let store = TestStore(
initialState: state,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: storage))
)
store.dependencies.derivationTool = .noop
store.dependencies.derivationTool = .noOp
store.dependencies.derivationTool.deriveSpendingKeys = { _, _ in [""] }
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
store.dependencies.mnemonic = .mock
store.dependencies.sdkSynchronizer = SDKSynchronizerDependency.mock
store.dependencies.walletStorage.exportWallet = { .placeholder }
// simulate the sending confirmation button to be pressed
store.send(.sendConfirmationPressed) { state in
@ -145,22 +143,19 @@ class SendTests: XCTestCase {
}
func testSendFailed() throws {
// the test needs to pass the exportWallet() so we simulate some in the keychain
try storage.importWallet(bip39: "one two three", birthday: nil)
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: storage))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.derivationTool = .noop
store.dependencies.derivationTool = .noOp
store.dependencies.derivationTool.deriveSpendingKeys = { _, _ in [""] }
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
store.dependencies.mnemonic = .mock
store.dependencies.walletStorage.exportWallet = { .placeholder }
// simulate the sending confirmation button to be pressed
store.send(.sendConfirmationPressed) { state in
@ -190,17 +185,12 @@ class SendTests: XCTestCase {
}
func testAddressValidation() throws {
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.derivationTool = .noop
store.dependencies.derivationTool = .noOp
store.dependencies.derivationTool.isValidZcashAddress = { _ in false }
store.send(.transactionAddressInput(.textField(.set("3HRG769ii3HDSJV5vNknQPzXqtL2mTSGnr")))) { state in
@ -231,16 +221,13 @@ class SendTests: XCTestCase {
}
func testInvalidAmountFormatEmptyInput() throws {
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.numberFormatter = .noOp
// Checks the computed property `isInvalidAmountFormat` which controls the error message to be shown on the screen
// With empty input it must be false
store.send(.transactionAmountInput(.textField(.set(""))))
@ -249,17 +236,12 @@ class SendTests: XCTestCase {
}
func testInvalidAddressFormatEmptyInput() throws {
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: .placeholder,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.derivationTool = .noop
store.dependencies.derivationTool = .noOp
// Checks the computed property `isInvalidAddressFormat` which controls the error message to be shown on the screen
// With empty input it must be false
@ -291,17 +273,15 @@ class SendTests: XCTestCase {
)
)
)
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.numberFormatter = .noOp
store.dependencies.numberFormatter.number = { _ in NSNumber(0.00501299) }
store.send(.transactionAmountInput(.textField(.set("0.00501299")))) { state in
state.transactionAmountInputState.textFieldState.text = "0.00501299"
state.transactionAmountInputState.textFieldState.valid = true
@ -315,6 +295,8 @@ class SendTests: XCTestCase {
state.transactionAmountInputState.amount = 501_299
}
store.dependencies.numberFormatter.number = { _ in NSNumber(0.00501301) }
store.send(.transactionAmountInput(.textField(.set("0.00501301")))) { state in
state.transactionAmountInputState.textFieldState.text = "0.00501301"
state.transactionAmountInputState.textFieldState.valid = true
@ -333,43 +315,19 @@ class SendTests: XCTestCase {
}
}
func testDifferentAmountFormats() throws {
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: .init(
addMemoState: true,
memoState: .placeholder,
route: nil,
transactionAddressInputState: .placeholder,
transactionAmountInputState:
TransactionAmountTextFieldReducer.State(
currencySelectionState: CurrencySelectionReducer.State(),
textFieldState:
TCATextFieldReducer.State(
validationType: .customFloatingPoint(usNumberFormatter),
text: ""
)
)
),
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
try amountFormatTest("1.234", true, 123_400_000, false, store)
try amountFormatTest("1,234", true, 123_400_000_000, false, store)
try amountFormatTest("1 234", true, 123_400_000_000, true, store)
try amountFormatTest("1,234.567", true, 123_456_700_000, false, store)
try amountFormatTest("1.", true, 100_000_000, false, store)
try amountFormatTest("1..", false, 0, false, store)
try amountFormatTest("1,.", false, 0, true, store)
try amountFormatTest("1.,", false, 0, true, store)
try amountFormatTest("1,,", false, 0, true, store)
try amountFormatTest("1,23", false, 0, true, store)
try amountFormatTest("1 23", false, 0, true, store)
try amountFormatTest("1.2.3", false, 0, true, store)
func testDifferentNumberFormats_LiveNumberFormatter() throws {
try numberFormatTest("1.234", NSNumber(1.234))
try numberFormatTest("1,234", NSNumber(1_234))
try numberFormatTest("1 234", NSNumber(1_234))
try numberFormatTest("1,234.567", NSNumber(1_234.567))
try numberFormatTest("1.", NSNumber(1))
try numberFormatTest("1..", nil)
try numberFormatTest("1,.", nil)
try numberFormatTest("1.,", nil)
try numberFormatTest("1,,", nil)
try numberFormatTest("1,23", nil)
try numberFormatTest("1 23", nil)
try numberFormatTest("1.2.3", nil)
}
func testValidForm() throws {
@ -389,18 +347,13 @@ class SendTests: XCTestCase {
)
)
)
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.derivationTool = .noop
store.dependencies.derivationTool = .noOp
store.dependencies.derivationTool.isValidZcashAddress = { _ in true }
store.send(.transactionAddressInput(.textField(.set("t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po")))) { state in
@ -433,17 +386,12 @@ class SendTests: XCTestCase {
)
)
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.derivationTool = .noop
store.dependencies.derivationTool = .noOp
store.dependencies.derivationTool.isValidZcashAddress = { _ in true }
store.send(.transactionAddressInput(.textField(.set("t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po")))) { state in
@ -475,18 +423,13 @@ class SendTests: XCTestCase {
)
)
)
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.derivationTool = .noop
store.dependencies.derivationTool = .noOp
store.send(.transactionAddressInput(.textField(.set("3HRG769ii3HDSJV5vNknQPzXqtL2mTSGnr")))) { state in
state.transactionAddressInputState.textFieldState.text = "3HRG769ii3HDSJV5vNknQPzXqtL2mTSGnr"
@ -518,20 +461,16 @@ class SendTests: XCTestCase {
)
)
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.dependencies.derivationTool = .liveValue
store.send(.transactionAddressInput(.textField(.set("t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po")))) { state in
state.transactionAddressInputState.textFieldState.text = "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po"
store.dependencies.derivationTool = .noOp
store.dependencies.derivationTool.isValidZcashAddress = { _ in true }
store.send(.transactionAddressInput(.textField(.set("tmGh6ttAnQRJra81moqYcedFadW9XtUT5Eq")))) { state in
state.transactionAddressInputState.textFieldState.text = "tmGh6ttAnQRJra81moqYcedFadW9XtUT5Eq"
// true is expected here because textField doesn't have any `validationType: String.ValidationType?`
// isValid function returns true, `guard let validationType = validationType else { return true }`
state.transactionAddressInputState.textFieldState.valid = true
@ -569,17 +508,12 @@ class SendTests: XCTestCase {
)
)
)
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.memo(.binding(.set(\.$text, "test")))) { state in
state.memoState.text = "test"
XCTAssertFalse(
@ -606,14 +540,9 @@ class SendTests: XCTestCase {
)
)
let testScheduler = DispatchQueue.test
let store = TestStore(
initialState: sendState,
reducer: SendFlowReducer()
.dependency(\.mainQueue, testScheduler.eraseToAnyScheduler())
.dependency(\.walletStorage, .live(walletStorage: WalletStorage(secItem: .live)))
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
)
store.send(.onAppear) { state in
@ -629,24 +558,15 @@ class SendTests: XCTestCase {
}
private extension SendTests {
func amountFormatTest(
func numberFormatTest(
_ amount: String,
_ expectedValidationResult: Bool,
_ expectedAmount: Int64,
_ expectedToReceive: Bool,
_ store: TestStore<SendFlowReducer.State, SendFlowReducer.Action, SendFlowReducer.State, SendFlowReducer.Action, Void>
_ expectedResult: NSNumber?
) throws {
store.send(.transactionAmountInput(.textField(.set(amount)))) { state in
state.transactionAmountInputState.textFieldState.text = amount
state.transactionAmountInputState.textFieldState.valid = expectedValidationResult
}
if expectedToReceive {
store.receive(.transactionAmountInput(.updateAmount))
if let number = NumberFormatterClient.liveValue.number(amount) {
XCTAssertEqual(number, expectedResult)
return
} else {
store.receive(.transactionAmountInput(.updateAmount)) { state in
state.transactionAmountInputState.amount = expectedAmount
}
XCTAssertEqual(nil, expectedResult, "NumberFormatterClient.liveValue.number(\(amount)) unexpected result.")
}
}
}

View File

@ -34,9 +34,11 @@ class TransactionAmountTextFieldTests: XCTestCase {
)
),
reducer: TransactionAmountTextFieldReducer()
.dependency(\.numberFormatter, .live(numberFormatter: usNumberFormatter))
)
store.dependencies.numberFormatter.string = { self.usNumberFormatter.string(from: $0) }
store.dependencies.numberFormatter.number = { self.usNumberFormatter.number(from: $0) }
store.send(.setMax) { state in
state.textFieldState.text = "0.00501301"
}
@ -83,9 +85,11 @@ class TransactionAmountTextFieldTests: XCTestCase {
zecPrice: 1000.0
),
reducer: TransactionAmountTextFieldReducer()
.dependency(\.numberFormatter, .live(numberFormatter: usNumberFormatter))
)
store.dependencies.numberFormatter.string = { self.usNumberFormatter.string(from: $0) }
store.dependencies.numberFormatter.number = { self.usNumberFormatter.number(from: $0) }
store.send(.currencySelection(.swapCurrencyType)) { state in
state.textFieldState.text = "1,000"
state.currencySelectionState.currencyType = .usd
@ -112,9 +116,11 @@ class TransactionAmountTextFieldTests: XCTestCase {
zecPrice: 1000.0
),
reducer: TransactionAmountTextFieldReducer()
.dependency(\.numberFormatter, .live(numberFormatter: usNumberFormatter))
)
store.dependencies.numberFormatter.string = { self.usNumberFormatter.string(from: $0) }
store.dependencies.numberFormatter.number = { self.usNumberFormatter.number(from: $0) }
store.send(.currencySelection(.swapCurrencyType)) { state in
state.textFieldState.text = "25,000,000"
state.currencySelectionState.currencyType = .usd
@ -141,9 +147,11 @@ class TransactionAmountTextFieldTests: XCTestCase {
zecPrice: 1000.0
),
reducer: TransactionAmountTextFieldReducer()
.dependency(\.numberFormatter, .live(numberFormatter: usNumberFormatter))
)
store.dependencies.numberFormatter.string = { self.usNumberFormatter.string(from: $0) }
store.dependencies.numberFormatter.number = { self.usNumberFormatter.number(from: $0) }
store.send(.currencySelection(.swapCurrencyType)) { state in
state.textFieldState.text = "1"
state.currencySelectionState.currencyType = .zec
@ -171,9 +179,11 @@ class TransactionAmountTextFieldTests: XCTestCase {
zecPrice: 1000.0
),
reducer: TransactionAmountTextFieldReducer()
.dependency(\.numberFormatter, .live(numberFormatter: usNumberFormatter))
)
store.dependencies.numberFormatter.string = { self.usNumberFormatter.string(from: $0) }
store.dependencies.numberFormatter.number = { self.usNumberFormatter.number(from: $0) }
store.send(.textField(.set("1 000"))) { state in
state.textFieldState.text = "1 000"
state.textFieldState.valid = true
@ -210,9 +220,11 @@ class TransactionAmountTextFieldTests: XCTestCase {
zecPrice: 1000.0
),
reducer: TransactionAmountTextFieldReducer()
.dependency(\.numberFormatter, .live(numberFormatter: usNumberFormatter))
)
store.dependencies.numberFormatter.string = { self.usNumberFormatter.string(from: $0) }
store.dependencies.numberFormatter.number = { self.usNumberFormatter.number(from: $0) }
store.send(.setMax) { state in
state.textFieldState.text = "2"
}
@ -239,9 +251,11 @@ class TransactionAmountTextFieldTests: XCTestCase {
zecPrice: 1000.0
),
reducer: TransactionAmountTextFieldReducer()
.dependency(\.numberFormatter, .live(numberFormatter: usNumberFormatter))
)
store.dependencies.numberFormatter.string = { self.usNumberFormatter.string(from: $0) }
store.dependencies.numberFormatter.number = { self.usNumberFormatter.number(from: $0) }
store.send(.setMax) { state in
state.textFieldState.text = "2,000"
}

View File

@ -20,7 +20,7 @@ class SettingsTests: XCTestCase {
breeze blouse charge solid fish spread
"""
let mockedWalletStorage = WrappedWalletStorage(
let mockedWalletStorage = WalletStorageClient(
importWallet: { _, _, _, _ in
throw WalletStorage.WalletStorageError.alreadyImported
},
@ -47,11 +47,13 @@ class SettingsTests: XCTestCase {
let store = TestStore(
initialState: SettingsReducer.State(phraseDisplayState: RecoveryPhraseDisplayReducer.State(phrase: nil)),
reducer: SettingsReducer()
.dependency(\.walletStorage, mockedWalletStorage)
)
store.dependencies.localAuthentication = .mockAuthenticationSucceeded
store.dependencies.mnemonic = .noOp
store.dependencies.mnemonic.asWords = { _ in mnemonic.components(separatedBy: " ") }
store.dependencies.walletStorage = mockedWalletStorage
_ = await store.send(.backupWalletAccessRequest)
await store.receive(.backupWallet) { state in

View File

@ -12,7 +12,7 @@ import ComposableArchitecture
class HomeCircularProgressSnapshotTests: XCTestCase {
func testCircularProgress_DownloadingInnerCircle() throws {
class SnapshotTestWrappedSDKSynchronizer: TestWrappedSDKSynchronizer {
class SnapshotNoopSDKSynchronizer: NoopSDKSynchronizer {
// heights purposely set so we visually see 55% progress
override func statusSnapshot() -> SyncStatusSnapshot {
let blockProgress = BlockProgress(
@ -40,15 +40,15 @@ class HomeCircularProgressSnapshotTests: XCTestCase {
walletEventsState: .emptyPlaceHolder
),
reducer: HomeReducer()
.dependency(\.sdkSynchronizer, SnapshotTestWrappedSDKSynchronizer())
.dependency(\.diskSpaceChecker, .mockEmptyDisk)
.dependency(\.sdkSynchronizer, SnapshotNoopSDKSynchronizer())
)
addAttachments(HomeView(store: store))
}
func testCircularProgress_ScanningOuterCircle() throws {
class SnapshotTestWrappedSDKSynchronizer: TestWrappedSDKSynchronizer {
class SnapshotNoopSDKSynchronizer: NoopSDKSynchronizer {
override func statusSnapshot() -> SyncStatusSnapshot {
// heights purposely set so we visually see 72% progress
let blockProgress = BlockProgress(
@ -83,7 +83,7 @@ class HomeCircularProgressSnapshotTests: XCTestCase {
}
func testCircularProgress_UpToDateOnlyOuterCircle() throws {
class SnapshotTestWrappedSDKSynchronizer: TestWrappedSDKSynchronizer {
class SnapshotNoopSDKSynchronizer: NoopSDKSynchronizer {
override func statusSnapshot() -> SyncStatusSnapshot {
SyncStatusSnapshot.snapshotFor(state: .synced)
}

View File

@ -15,8 +15,8 @@ class ProfileSnapshotTests: XCTestCase {
let store = Store(
initialState: .placeholder,
reducer: ProfileReducer()
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
.dependency(\.appVersion, .mock)
.dependency(\.sdkSynchronizer, NoopSDKSynchronizer())
)
ViewStore(store).send(.onAppear)

View File

@ -24,7 +24,8 @@ class RecoveryPhraseValidationFlowSnapshotTests: XCTestCase {
let store = RecoveryPhraseValidationFlowStore(
initialState: .placeholder,
reducer: RecoveryPhraseValidationFlowReducer()
.dependency(\.mainQueue, RecoveryPhraseValidationTests.testScheduler.eraseToAnyScheduler())
.dependency(\.feedbackGenerator, .noOp)
.dependency(\.mainQueue, DispatchQueue.test.eraseToAnyScheduler())
)
let viewStore = ViewStore(store)

View File

@ -34,8 +34,8 @@ class TransactionConfirmationSnapshotTests: XCTestCase {
reducer: SendFlowReducer()
.dependency(\.derivationTool, .live(derivationTool: DerivationTool(networkType: .testnet)))
.dependency(\.mainQueue, DispatchQueue.main.eraseToAnyScheduler())
.dependency(\.walletStorage, .live())
.dependency(\.numberFormatter, .live())
.dependency(\.walletStorage, .live())
)
ViewStore(store).send(.onAppear)
@ -64,8 +64,8 @@ class TransactionConfirmationSnapshotTests: XCTestCase {
reducer: SendFlowReducer()
.dependency(\.derivationTool, .live(derivationTool: DerivationTool(networkType: .testnet)))
.dependency(\.mainQueue, DispatchQueue.main.eraseToAnyScheduler())
.dependency(\.walletStorage, .live())
.dependency(\.numberFormatter, .live())
.dependency(\.walletStorage, .live())
)
ViewStore(store).send(.onAppear)

View File

@ -1,5 +1,5 @@
//
// TransactionSendingTests.swift
// TransactionSendingSnapshotTests.swift
// secantTests
//
// Created by Michal Fousek on 30.09.2022.
@ -34,8 +34,8 @@ class TransactionSendingTests: XCTestCase {
reducer: SendFlowReducer()
.dependency(\.derivationTool, .live(derivationTool: DerivationTool(networkType: .testnet)))
.dependency(\.mainQueue, DispatchQueue.main.eraseToAnyScheduler())
.dependency(\.walletStorage, .live())
.dependency(\.numberFormatter, .live())
.dependency(\.walletStorage, .live())
)
ViewStore(store).send(.onAppear)

View File

@ -16,8 +16,8 @@ class SettingsSnapshotTests: XCTestCase {
initialState: .placeholder,
reducer: SettingsReducer()
.dependency(\.localAuthentication, .mockAuthenticationFailed)
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
.dependency(\.walletStorage, .throwing)
.dependency(\.sdkSynchronizer, NoopSDKSynchronizer())
.dependency(\.walletStorage, .noOp)
)
addAttachments(SettingsView(store: store))

View File

@ -30,7 +30,7 @@ class DatabaseFilesTests: XCTestCase {
let network = ZcashNetworkBuilder.network(for: .testnet)
func testFailingDocumentsDirectory() throws {
let mockedFileManager = WrappedFileManager(
let mockedFileManager = FileManagerClient(
url: { _, _, _, _ in throw "some error" },
fileExists: { _ in return true },
removeItem: { _ in }
@ -58,7 +58,7 @@ class DatabaseFilesTests: XCTestCase {
}
func testFailingDataDbURL() throws {
let mockedFileManager = WrappedFileManager(
let mockedFileManager = FileManagerClient(
url: { _, _, _, _ in throw "some error" },
fileExists: { _ in return true },
removeItem: { _ in }
@ -86,8 +86,8 @@ class DatabaseFilesTests: XCTestCase {
}
func testDatabaseFilesPresent() throws {
let mockedFileManager = WrappedFileManager(
url: { _, _, _, _ in URL(fileURLWithPath: "") },
let mockedFileManager = FileManagerClient(
url: { _, _, _, _ in .emptyURL },
fileExists: { _ in return true },
removeItem: { _ in }
)
@ -104,8 +104,8 @@ class DatabaseFilesTests: XCTestCase {
}
func testDatabaseFilesNotPresent() throws {
let mockedFileManager = WrappedFileManager(
url: { _, _, _, _ in URL(fileURLWithPath: "") },
let mockedFileManager = FileManagerClient(
url: { _, _, _, _ in .emptyURL },
fileExists: { _ in return false },
removeItem: { _ in }
)
@ -122,7 +122,7 @@ class DatabaseFilesTests: XCTestCase {
}
func testDatabaseFilesPresentFailure() throws {
let mockedFileManager = WrappedFileManager(
let mockedFileManager = FileManagerClient(
url: { _, _, _, _ in throw "some error" },
fileExists: { _ in return true },
removeItem: { _ in }
@ -150,8 +150,8 @@ class DatabaseFilesTests: XCTestCase {
}
func testNukeFiles_RemoveFileFailure() throws {
let mockedFileManager = WrappedFileManager(
url: { _, _, _, _ in URL(fileURLWithPath: "") },
let mockedFileManager = FileManagerClient(
url: { _, _, _, _ in .emptyURL },
fileExists: { _ in return true },
removeItem: { _ in throw "some error" }
)
@ -178,7 +178,7 @@ class DatabaseFilesTests: XCTestCase {
}
func testNukeFiles_URLFailure() throws {
let mockedFileManager = WrappedFileManager(
let mockedFileManager = FileManagerClient(
url: { _, _, _, _ in throw "some error" },
fileExists: { _ in return true },
removeItem: { _ in }

View File

@ -1,5 +1,5 @@
//
// WrappedSecItemTests.swift
// SecItemClientTests.swift
// secantTests
//
// Created by Lukáš Korba on 12.04.2022.
@ -20,9 +20,9 @@ extension WalletStorage.KeychainError {
}
}
class WrappedSecItemTests: XCTestCase {
class SecItemClientTests: XCTestCase {
func test_secItemAdd_KeychainErrorDuplicate() throws {
let secItemDuplicate = WrappedSecItem(
let secItemDuplicate = SecItemClient(
copyMatching: { _, _ in errSecSuccess },
add: { _, _ in errSecDuplicateItem },
update: { _, _ in errSecSuccess },
@ -34,10 +34,10 @@ class WrappedSecItemTests: XCTestCase {
do {
try walletStorage.setData(Data(), forKey: "")
XCTFail("WrappedSecItem: test_secItemAdd_KeychainErrorDuplicate expected to fail but passed.")
XCTFail("SecItemClient: test_secItemAdd_KeychainErrorDuplicate expected to fail but passed.")
} catch {
guard let error = error as? WalletStorage.KeychainError else {
XCTFail("WrappedSecItem: the error is expected to be WalletStorage.KeychainError but it's \(error).")
XCTFail("SecItemClient: the error is expected to be WalletStorage.KeychainError but it's \(error).")
return
}
@ -45,13 +45,13 @@ class WrappedSecItemTests: XCTestCase {
XCTAssertEqual(
error.debugValue,
WalletStorage.KeychainError.duplicate.debugValue,
"WrappedSecItem: error must be .duplicate but it's \(error)."
"SecItemClient: error must be .duplicate but it's \(error)."
)
}
}
func test_secItemAdd_KeychainErrorUnknown() throws {
let secItemDuplicate = WrappedSecItem(
let secItemDuplicate = SecItemClient(
copyMatching: { _, _ in errSecSuccess },
add: { _, _ in errSecCoreFoundationUnknown },
update: { _, _ in errSecSuccess },
@ -63,10 +63,10 @@ class WrappedSecItemTests: XCTestCase {
do {
try walletStorage.setData(Data(), forKey: "")
XCTFail("WrappedSecItem: test_secItemAdd_KeychainErrorUnknown expected to fail but passed.")
XCTFail("SecItemClient: test_secItemAdd_KeychainErrorUnknown expected to fail but passed.")
} catch {
guard let error = error as? WalletStorage.KeychainError else {
XCTFail("WrappedSecItem: the error is expected to be WalletStorage.KeychainError but it's \(error).")
XCTFail("SecItemClient: the error is expected to be WalletStorage.KeychainError but it's \(error).")
return
}
@ -74,13 +74,13 @@ class WrappedSecItemTests: XCTestCase {
XCTAssertEqual(
error.debugValue,
WalletStorage.KeychainError.unknown(0).debugValue,
"WrappedSecItem: error must be .unknown but it's \(error)."
"SecItemClient: error must be .unknown but it's \(error)."
)
}
}
func test_secItemUpdate_KeychainErrorNoDataFound() throws {
let secItemDuplicate = WrappedSecItem(
let secItemDuplicate = SecItemClient(
copyMatching: { _, _ in errSecSuccess },
add: { _, _ in errSecSuccess },
update: { _, _ in errSecItemNotFound },
@ -92,10 +92,10 @@ class WrappedSecItemTests: XCTestCase {
do {
try walletStorage.updateData(Data(), forKey: "")
XCTFail("WrappedSecItem: test_secItemUpdate_KeychainErrorNoDataFound expected to fail but passed.")
XCTFail("SecItemClient: test_secItemUpdate_KeychainErrorNoDataFound expected to fail but passed.")
} catch {
guard let error = error as? WalletStorage.KeychainError else {
XCTFail("WrappedSecItem: the error is expected to be WalletStorage.KeychainError but it's \(error).")
XCTFail("SecItemClient: the error is expected to be WalletStorage.KeychainError but it's \(error).")
return
}
@ -103,13 +103,13 @@ class WrappedSecItemTests: XCTestCase {
XCTAssertEqual(
error.debugValue,
WalletStorage.KeychainError.noDataFound.debugValue,
"WrappedSecItem: error must be .noDataFound but it's \(error)."
"SecItemClient: error must be .noDataFound but it's \(error)."
)
}
}
func test_secItemUpdate_KeychainErrorUnknown() throws {
let secItemDuplicate = WrappedSecItem(
let secItemDuplicate = SecItemClient(
copyMatching: { _, _ in errSecSuccess },
add: { _, _ in errSecSuccess },
update: { _, _ in errSecCoreFoundationUnknown },
@ -121,10 +121,10 @@ class WrappedSecItemTests: XCTestCase {
do {
try walletStorage.updateData(Data(), forKey: "")
XCTFail("WrappedSecItem: test_secItemUpdate_KeychainErrorUnknown expected to fail but passed.")
XCTFail("SecItemClient: test_secItemUpdate_KeychainErrorUnknown expected to fail but passed.")
} catch {
guard let error = error as? WalletStorage.KeychainError else {
XCTFail("WrappedSecItem: the error is expected to be WalletStorage.KeychainError but it's \(error).")
XCTFail("SecItemClient: the error is expected to be WalletStorage.KeychainError but it's \(error).")
return
}
@ -132,13 +132,13 @@ class WrappedSecItemTests: XCTestCase {
XCTAssertEqual(
error.debugValue,
WalletStorage.KeychainError.unknown(0).debugValue,
"WrappedSecItem: error must be .unknown but it's \(error)."
"SecItemClient: error must be .unknown but it's \(error)."
)
}
}
func test_secItemDelete_Succeeded() throws {
let secItemDuplicate = WrappedSecItem(
let secItemDuplicate = SecItemClient(
copyMatching: { _, _ in errSecSuccess },
add: { _, _ in errSecSuccess },
update: { _, _ in errSecSuccess },
@ -153,7 +153,7 @@ class WrappedSecItemTests: XCTestCase {
}
func test_secItemDelete_Failed() throws {
let secItemDuplicate = WrappedSecItem(
let secItemDuplicate = SecItemClient(
copyMatching: { _, _ in errSecSuccess },
add: { _, _ in errSecSuccess },
update: { _, _ in errSecSuccess },

View File

@ -94,7 +94,7 @@ class UserPreferencesStorageTests: XCTestCase {
// MARK: - Mocked user defaults vs. default values
func testAppSessionFrom_mocked() throws {
let mockedUD = WrappedUserDefaults(
let mockedUD = UserDefaultsClient(
objectForKey: { _ in 87654321.0 },
remove: { _ in },
setValue: { _, _ in },
@ -114,7 +114,7 @@ class UserPreferencesStorageTests: XCTestCase {
}
func testConvertedCurrency_mocked() throws {
let mockedUD = WrappedUserDefaults(
let mockedUD = UserDefaultsClient(
objectForKey: { _ in "CZK" },
remove: { _ in },
setValue: { _, _ in },
@ -134,7 +134,7 @@ class UserPreferencesStorageTests: XCTestCase {
}
func testFiatConvertion_mocked() throws {
let mockedUD = WrappedUserDefaults(
let mockedUD = UserDefaultsClient(
objectForKey: { _ in false },
remove: { _ in },
setValue: { _, _ in },
@ -154,7 +154,7 @@ class UserPreferencesStorageTests: XCTestCase {
}
func testRecoveryPhraseTestCompleted_mocked() throws {
let mockedUD = WrappedUserDefaults(
let mockedUD = UserDefaultsClient(
objectForKey: { _ in false },
remove: { _ in },
setValue: { _, _ in },
@ -174,7 +174,7 @@ class UserPreferencesStorageTests: XCTestCase {
}
func testSessionAutoshielded_mocked() throws {
let mockedUD = WrappedUserDefaults(
let mockedUD = UserDefaultsClient(
objectForKey: { _ in true },
remove: { _ in },
setValue: { _, _ in },

View File

@ -76,10 +76,11 @@ class WalletEventsTests: XCTestCase {
walletEvents: identifiedWalletEvents
),
reducer: WalletEventsFlowReducer()
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
.dependency(\.mainQueue, WalletEventsTests.testScheduler.eraseToAnyScheduler())
)
store.dependencies.mainQueue = Self.testScheduler.eraseToAnyScheduler()
store.dependencies.sdkSynchronizer = SDKSynchronizerDependency.mock
store.send(.synchronizerStateChanged(.synced))
Self.testScheduler.advance(by: 0.01)
@ -98,8 +99,6 @@ class WalletEventsTests: XCTestCase {
}
func testCopyToPasteboard() throws {
let pasteboard = WrappedPasteboard.test
let store = TestStore(
initialState: WalletEventsFlowReducer.State(
route: .latest,
@ -108,10 +107,16 @@ class WalletEventsTests: XCTestCase {
),
reducer: WalletEventsFlowReducer()
)
store.dependencies.pasteboard = .testPasteboard
let testText = "test text"
store.send(.copyToPastboard(testText))
XCTAssertEqual(pasteboard.getString(), testText, "WalletEvetns: `testCopyToPasteboard` is expected to match the input `\(testText)`")
XCTAssertEqual(
store.dependencies.pasteboard.getString(),
testText,
"WalletEvetns: `testCopyToPasteboard` is expected to match the input `\(testText)`"
)
}
}