diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj index 878638ac..4334c76f 100644 --- a/secant.xcodeproj/project.pbxproj +++ b/secant.xcodeproj/project.pbxproj @@ -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 = ""; }; 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldFooter.swift; sourceTree = ""; }; 2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugFrame.swift; sourceTree = ""; }; - 34429C6D28E703CD00F2B929 /* TransactionSendingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionSendingTests.swift; sourceTree = ""; }; + 34429C6D28E703CD00F2B929 /* TransactionSendingSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionSendingSnapshotTests.swift; sourceTree = ""; }; 3448CB3128E47666006ADEDB /* NotEnoughFreeSpaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotEnoughFreeSpaceView.swift; sourceTree = ""; }; 3448CB3628E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotEnoughFeeSpaceSnapshots.swift; sourceTree = ""; }; 346715A428E2027D0035F7C4 /* CheckCircleStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckCircleStore.swift; sourceTree = ""; }; @@ -366,11 +387,22 @@ 66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = ""; }; 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = ""; }; 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = ""; }; - 9E01F8232833C0D8000EFC57 /* WrappedURIParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedURIParser.swift; sourceTree = ""; }; 9E01F8272833CDA0000EFC57 /* ScanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanTests.swift; sourceTree = ""; }; - 9E02B56927FED43E005B809B /* WrappedFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedFileManager.swift; sourceTree = ""; }; + 9E02B56927FED43E005B809B /* FileManagerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerInterface.swift; sourceTree = ""; }; 9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFilesTests.swift; sourceTree = ""; }; - 9E2AC10027D8EF0B0042AA47 /* WrappedMnemonic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedMnemonic.swift; sourceTree = ""; }; + 9E153A5B2920CD5100112F41 /* MnemonicMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicMocks.swift; sourceTree = ""; }; + 9E153A5C2920CD5100112F41 /* MnemonicLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicLiveKey.swift; sourceTree = ""; }; + 9E153A5D2920CD5100112F41 /* MnemonicTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicTestKey.swift; sourceTree = ""; }; + 9E153A5E2920CD5100112F41 /* MnemonicInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicInterface.swift; sourceTree = ""; }; + 9E153A6429210B3B00112F41 /* PasteboardLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasteboardLiveKey.swift; sourceTree = ""; }; + 9E153A6529210B3B00112F41 /* PasteboardInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasteboardInterface.swift; sourceTree = ""; }; + 9E153A6629210B3B00112F41 /* PasteboardTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasteboardTestKey.swift; sourceTree = ""; }; + 9E153A6B292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZcashSDKEnvironmentLiveKey.swift; sourceTree = ""; }; + 9E153A6C292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZcashSDKEnvironmentTestKey.swift; sourceTree = ""; }; + 9E153A6D292167FF00112F41 /* ZcashSDKEnvironmentInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZcashSDKEnvironmentInterface.swift; sourceTree = ""; }; + 9E153A7229216EFB00112F41 /* UserDefaultsLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsLiveKey.swift; sourceTree = ""; }; + 9E153A7329216EFB00112F41 /* UserDefaultsInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsInterface.swift; sourceTree = ""; }; + 9E153A7429216EFB00112F41 /* UserDefaultsTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsTestKey.swift; sourceTree = ""; }; 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletStore.swift; sourceTree = ""; }; 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportSeedEditor.swift; sourceTree = ""; }; 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = ""; }; @@ -382,12 +414,8 @@ 9E39112D283F91600073DD9A /* ZatoshiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZatoshiTests.swift; sourceTree = ""; }; 9E391131284644580073DD9A /* AppInitializationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInitializationTests.swift; sourceTree = ""; }; 9E3911382848AD500073DD9A /* HomeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTests.swift; sourceTree = ""; }; - 9E39113A2848D5180073DD9A /* WrappedNumberFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedNumberFormatter.swift; sourceTree = ""; }; - 9E39113E2848EC350073DD9A /* WrappedRecoveryPhraseRandomizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WrappedRecoveryPhraseRandomizer.swift; sourceTree = ""; }; 9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizer.swift; sourceTree = ""; }; - 9E3911432848EEB90073DD9A /* URIParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URIParser.swift; sourceTree = ""; }; 9E3911442848EEB90073DD9A /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = ""; }; - 9E3911452848EEB90073DD9A /* ZCashSDKEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZCashSDKEnvironment.swift; sourceTree = ""; }; 9E3911462848EEB90073DD9A /* DatabaseFiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = ""; }; 9E3911472848EEB90073DD9A /* WalletStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorage.swift; sourceTree = ""; }; 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphicDesignModifier.swift; sourceTree = ""; }; @@ -396,8 +424,7 @@ 9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEventsTests.swift; sourceTree = ""; }; 9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionFailedView.swift; sourceTree = ""; }; 9E5BF643281FEC9900BA3F17 /* SendTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendTests.swift; sourceTree = ""; }; - 9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedUserDefaults.swift; sourceTree = ""; }; - 9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedNotificationCenter.swift; sourceTree = ""; }; + 9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterInterface.swift; sourceTree = ""; }; 9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextField.swift; sourceTree = ""; }; 9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextFieldStore.swift; sourceTree = ""; }; 9E661229287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCircularProgressSnapshotTests.swift; sourceTree = ""; }; @@ -410,11 +437,6 @@ 9E6713F6289BC58C00A6796F /* BalanceBreakdownStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceBreakdownStore.swift; sourceTree = ""; }; 9E6713F9289BE0E100A6796F /* ClearBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearBackgroundView.swift; sourceTree = ""; }; 9E69A24C27FB002800A55317 /* WelcomeStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStore.swift; sourceTree = ""; }; - 9E6EF2CA291287BB00CA007B /* FeedbackGeneratorKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGeneratorKey.swift; sourceTree = ""; }; - 9E6EF2CC2913B06300CA007B /* NumberFormatterKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberFormatterKey.swift; sourceTree = ""; }; - 9E6EF2CE2913B11A00CA007B /* SDKSynchronizerKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerKey.swift; sourceTree = ""; }; - 9E6EF2D02913B75400CA007B /* MnemonicKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicKey.swift; sourceTree = ""; }; - 9E6EF2D22913B79A00CA007B /* WalletStorageKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletStorageKey.swift; sourceTree = ""; }; 9E7225F02889539300DF7F17 /* SettingsSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSnapshotTests.swift; sourceTree = ""; }; 9E7225F2288AB6DD00DF7F17 /* MultipleLineTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleLineTextField.swift; sourceTree = ""; }; 9E7225F5288AC71A00DF7F17 /* MultiLineTextFieldStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiLineTextFieldStore.swift; sourceTree = ""; }; @@ -428,8 +450,6 @@ 9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Readable.swift"; sourceTree = ""; }; 9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Chunked.swift"; sourceTree = ""; }; 9E7FE0D6282D286500C374E8 /* RecoveryPhrase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhrase.swift; sourceTree = ""; }; - 9E7FE0D8282D289B00C374E8 /* WrappedFeedbackGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedFeedbackGenerator.swift; sourceTree = ""; }; - 9E7FE0DA282D28F100C374E8 /* WrappedPasteboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedPasteboard.swift; sourceTree = ""; }; 9E7FE0DC282D298900C374E8 /* ValidationWord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidationWord.swift; sourceTree = ""; }; 9E7FE0DE282D2DD600C374E8 /* ZcashBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashBadge.swift; sourceTree = ""; }; 9E7FE0E5282E7B1100C374E8 /* StoredWallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredWallet.swift; sourceTree = ""; }; @@ -449,11 +469,37 @@ 9EAB46772860A1D2002904A0 /* WalletEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEvent.swift; sourceTree = ""; }; 9EAB46792861EA6A002904A0 /* TransactionRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionRowView.swift; sourceTree = ""; }; 9EAFEB812805793200199FC9 /* AppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTests.swift; sourceTree = ""; }; - 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = ""; }; - 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = ""; }; - 9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSDKSynchronizer.swift; sourceTree = ""; }; + 9EAFEB83280597B700199FC9 /* SecItemInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecItemInterface.swift; sourceTree = ""; }; + 9EAFEB852805A23100199FC9 /* SecItemClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecItemClientTests.swift; sourceTree = ""; }; + 9EAFEB872806E5AE00199FC9 /* SDKSynchronizerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerInterface.swift; sourceTree = ""; }; 9EAFEB8D2808183D00199FC9 /* SandboxView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxView.swift; sourceTree = ""; }; 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxStore.swift; sourceTree = ""; }; + 9EB863892922CC4D003D0F8B /* FeedbackGeneratorTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGeneratorTestKey.swift; sourceTree = ""; }; + 9EB8638A2922CC4D003D0F8B /* FeedbackGeneratorInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGeneratorInterface.swift; sourceTree = ""; }; + 9EB8638B2922CC4D003D0F8B /* FeedbackGeneratorLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGeneratorLiveKey.swift; sourceTree = ""; }; + 9EB863902922D035003D0F8B /* NumberFormatterInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberFormatterInterface.swift; sourceTree = ""; }; + 9EB863912922D035003D0F8B /* NumberFormatterLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberFormatterLiveKey.swift; sourceTree = ""; }; + 9EB863922922D036003D0F8B /* NumberFormatterTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberFormatterTestKey.swift; sourceTree = ""; }; + 9EB863972923935B003D0F8B /* WalletStorageTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTestKey.swift; sourceTree = ""; }; + 9EB863982923935B003D0F8B /* WalletStorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageInterface.swift; sourceTree = ""; }; + 9EB863992923935B003D0F8B /* WalletStorageLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageLiveKey.swift; sourceTree = ""; }; + 9EB8639F292398A8003D0F8B /* URIParserInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URIParserInterface.swift; sourceTree = ""; }; + 9EB863A0292398A8003D0F8B /* URIParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URIParser.swift; sourceTree = ""; }; + 9EB863A429239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerTestKey.swift; sourceTree = ""; }; + 9EB863A529239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerLiveKey.swift; sourceTree = ""; }; + 9EB863A629239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseRandomizerInterface.swift; sourceTree = ""; }; + 9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerLive.swift; sourceTree = ""; }; + 9EB863BA2923C6F8003D0F8B /* NotificationCenterLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterLive.swift; sourceTree = ""; }; + 9EB863BC2923C704003D0F8B /* NotificationCenterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterTest.swift; sourceTree = ""; }; + 9EB863BE2923C72C003D0F8B /* SecItemLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecItemLive.swift; sourceTree = ""; }; + 9EB863C02923C779003D0F8B /* URIParserLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIParserLive.swift; sourceTree = ""; }; + 9EB863C22923C807003D0F8B /* URIParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIParserTest.swift; sourceTree = ""; }; + 9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerTest.swift; sourceTree = ""; }; + 9EB863C62923C93B003D0F8B /* UserPreferencesStorageLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageLive.swift; sourceTree = ""; }; + 9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageMocks.swift; sourceTree = ""; }; + 9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerLive.swift; sourceTree = ""; }; + 9EB863CC2923CA28003D0F8B /* SDKSynchronizerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerTest.swift; sourceTree = ""; }; + 9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerMocks.swift; sourceTree = ""; }; 9EBDF946291D75B2000A1A05 /* DiskSpaceCheckerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerInterface.swift; sourceTree = ""; }; 9EBDF948291D75BF000A1A05 /* DiskSpaceCheckerLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerLiveKey.swift; sourceTree = ""; }; 9EBDF94A291D75C7000A1A05 /* DiskSpaceCheckerTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerTestKey.swift; sourceTree = ""; }; @@ -461,7 +507,6 @@ 9EBDF94F291E5E86000A1A05 /* DatabaseFilesTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFilesTestKey.swift; sourceTree = ""; }; 9EBDF951291E5E86000A1A05 /* DatabaseFilesInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFilesInterface.swift; sourceTree = ""; }; 9EBDF952291E5E86000A1A05 /* DatabaseFilesLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFilesLiveKey.swift; sourceTree = ""; }; - 9EBDF957291E6418000A1A05 /* DatabaseFilesMocks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseFilesMocks.swift; sourceTree = ""; }; 9EBDF95B291E657B000A1A05 /* DeeplinkLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkLiveKey.swift; sourceTree = ""; }; 9EBDF95C291E657B000A1A05 /* DeeplinkTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkTestKey.swift; sourceTree = ""; }; 9EBDF95D291E657B000A1A05 /* DeeplinkInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkInterface.swift; sourceTree = ""; }; @@ -486,12 +531,9 @@ 9EDDEA9F2829610D00B4100C /* CurrencySelectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencySelectionTests.swift; sourceTree = ""; }; 9EDDEAA02829610D00B4100C /* TransactionAmountInputTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAmountInputTests.swift; sourceTree = ""; }; 9EDDEAA12829610D00B4100C /* TransactionAddressInputTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressInputTests.swift; sourceTree = ""; }; - 9EF1082A29114B93003D8097 /* PasteboardKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardKey.swift; sourceTree = ""; }; - 9EF1082C29114BCD003D8097 /* NewRecoveryPhraseKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewRecoveryPhraseKey.swift; sourceTree = ""; }; 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = ""; }; 9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageTests.swift; sourceTree = ""; }; 9EF8135F27F043CC0075AF48 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 9EF8139027F191BF0075AF48 /* WrappedWalletStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedWalletStorage.swift; sourceTree = ""; }; 9EF8139B27F47AED0075AF48 /* InitializationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitializationState.swift; sourceTree = ""; }; F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = ""; }; F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = ""; }; @@ -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 = ""; @@ -912,23 +953,45 @@ path = ScanTests; sourceTree = ""; }; - 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 = ""; + }; + 9E153A6329210AF800112F41 /* Pasteboard */ = { + isa = PBXGroup; + children = ( + 9E153A6529210B3B00112F41 /* PasteboardInterface.swift */, + 9E153A6429210B3B00112F41 /* PasteboardLiveKey.swift */, + 9E153A6629210B3B00112F41 /* PasteboardTestKey.swift */, + ); + path = Pasteboard; + sourceTree = ""; + }; + 9E153A6A292167BF00112F41 /* ZcashSDKEnvironment */ = { + isa = PBXGroup; + children = ( + 9E153A6D292167FF00112F41 /* ZcashSDKEnvironmentInterface.swift */, + 9E153A6B292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift */, + 9E153A6C292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift */, + ); + path = ZcashSDKEnvironment; + sourceTree = ""; + }; + 9E153A7129216EBD00112F41 /* UserDefaults */ = { + isa = PBXGroup; + children = ( + 9E153A7329216EFB00112F41 /* UserDefaultsInterface.swift */, + 9E153A7229216EFB00112F41 /* UserDefaultsLiveKey.swift */, + 9E153A7429216EFB00112F41 /* UserDefaultsTestKey.swift */, + ); + path = UserDefaults; sourceTree = ""; }; 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 = ""; @@ -1360,6 +1425,109 @@ path = Sandbox; sourceTree = ""; }; + 9EB863882922CC0E003D0F8B /* FeedbackGenerator */ = { + isa = PBXGroup; + children = ( + 9EB8638A2922CC4D003D0F8B /* FeedbackGeneratorInterface.swift */, + 9EB8638B2922CC4D003D0F8B /* FeedbackGeneratorLiveKey.swift */, + 9EB863892922CC4D003D0F8B /* FeedbackGeneratorTestKey.swift */, + ); + path = FeedbackGenerator; + sourceTree = ""; + }; + 9EB8638F2922D000003D0F8B /* NumberFormatter */ = { + isa = PBXGroup; + children = ( + 9EB863902922D035003D0F8B /* NumberFormatterInterface.swift */, + 9EB863912922D035003D0F8B /* NumberFormatterLiveKey.swift */, + 9EB863922922D036003D0F8B /* NumberFormatterTestKey.swift */, + ); + path = NumberFormatter; + sourceTree = ""; + }; + 9EB86396292392F6003D0F8B /* WalletStorage */ = { + isa = PBXGroup; + children = ( + 9E3911472848EEB90073DD9A /* WalletStorage.swift */, + 9EB863982923935B003D0F8B /* WalletStorageInterface.swift */, + 9EB863992923935B003D0F8B /* WalletStorageLiveKey.swift */, + 9EB863972923935B003D0F8B /* WalletStorageTestKey.swift */, + ); + path = WalletStorage; + sourceTree = ""; + }; + 9EB8639E29239891003D0F8B /* URIParser */ = { + isa = PBXGroup; + children = ( + 9EB863A0292398A8003D0F8B /* URIParser.swift */, + 9EB8639F292398A8003D0F8B /* URIParserInterface.swift */, + 9EB863C02923C779003D0F8B /* URIParserLive.swift */, + 9EB863C22923C807003D0F8B /* URIParserTest.swift */, + ); + path = URIParser; + sourceTree = ""; + }; + 9EB863A329239D95003D0F8B /* RecoveryPhraseRandomizer */ = { + isa = PBXGroup; + children = ( + 9E3911422848EEB90073DD9A /* RecoveryPhraseRandomizer.swift */, + 9EB863A629239DCB003D0F8B /* RecoveryPhraseRandomizerInterface.swift */, + 9EB863A529239DCB003D0F8B /* RecoveryPhraseRandomizerLiveKey.swift */, + 9EB863A429239DCB003D0F8B /* RecoveryPhraseRandomizerTestKey.swift */, + ); + path = RecoveryPhraseRandomizer; + sourceTree = ""; + }; + 9EB863B32923C465003D0F8B /* SecItem */ = { + isa = PBXGroup; + children = ( + 9EAFEB83280597B700199FC9 /* SecItemInterface.swift */, + 9EB863BE2923C72C003D0F8B /* SecItemLive.swift */, + ); + path = SecItem; + sourceTree = ""; + }; + 9EB863B42923C490003D0F8B /* NotificationCenter */ = { + isa = PBXGroup; + children = ( + 9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */, + 9EB863BA2923C6F8003D0F8B /* NotificationCenterLive.swift */, + 9EB863BC2923C704003D0F8B /* NotificationCenterTest.swift */, + ); + path = NotificationCenter; + sourceTree = ""; + }; + 9EB863B52923C4ED003D0F8B /* FileManager */ = { + isa = PBXGroup; + children = ( + 9E02B56927FED43E005B809B /* FileManagerInterface.swift */, + 9EB863B82923C6D7003D0F8B /* FileManagerLive.swift */, + 9EB863C42923C8AF003D0F8B /* FileManagerTest.swift */, + ); + path = FileManager; + sourceTree = ""; + }; + 9EB863B62923C539003D0F8B /* SDKSynchronizer */ = { + isa = PBXGroup; + children = ( + 9EAFEB872806E5AE00199FC9 /* SDKSynchronizerInterface.swift */, + 9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */, + 9EB863CC2923CA28003D0F8B /* SDKSynchronizerTest.swift */, + 9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */, + ); + path = SDKSynchronizer; + sourceTree = ""; + }; + 9EB863B72923C55A003D0F8B /* UserPreferencesStorage */ = { + isa = PBXGroup; + children = ( + 9E3911442848EEB90073DD9A /* UserPreferencesStorage.swift */, + 9EB863C62923C93B003D0F8B /* UserPreferencesStorageLive.swift */, + 9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */, + ); + path = UserPreferencesStorage; + sourceTree = ""; + }; 9EBDF945291D759B000A1A05 /* DiskSpaceChecker */ = { isa = PBXGroup; children = ( @@ -1379,7 +1547,6 @@ 9EBDF951291E5E86000A1A05 /* DatabaseFilesInterface.swift */, 9EBDF952291E5E86000A1A05 /* DatabaseFilesLiveKey.swift */, 9EBDF94F291E5E86000A1A05 /* DatabaseFilesTestKey.swift */, - 9EBDF957291E6418000A1A05 /* DatabaseFilesMocks.swift */, ); path = DatabaseFiles; sourceTree = ""; @@ -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 */, diff --git a/secant/Dependencies/CaptureDevice/CaptureDeviceTestKey.swift b/secant/Dependencies/CaptureDevice/CaptureDeviceTestKey.swift index fb3d3c66..7405f059 100644 --- a/secant/Dependencies/CaptureDevice/CaptureDeviceTestKey.swift +++ b/secant/Dependencies/CaptureDevice/CaptureDeviceTestKey.swift @@ -16,7 +16,7 @@ extension CaptureDeviceClient: TestDependencyKey { } extension CaptureDeviceClient { - static let noop = Self( + static let noOp = Self( isTorchAvailable: { false }, torch: { _ in } ) diff --git a/secant/Dependencies/DatabaseFiles/DatabaseFiles.swift b/secant/Dependencies/DatabaseFiles/DatabaseFiles.swift index ff6924e0..9bb56313 100644 --- a/secant/Dependencies/DatabaseFiles/DatabaseFiles.swift +++ b/secant/Dependencies/DatabaseFiles/DatabaseFiles.swift @@ -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 } diff --git a/secant/Dependencies/DatabaseFiles/DatabaseFilesInterface.swift b/secant/Dependencies/DatabaseFiles/DatabaseFilesInterface.swift index 8c4716d1..0422d724 100644 --- a/secant/Dependencies/DatabaseFiles/DatabaseFilesInterface.swift +++ b/secant/Dependencies/DatabaseFiles/DatabaseFilesInterface.swift @@ -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 } diff --git a/secant/Dependencies/DatabaseFiles/DatabaseFilesMocks.swift b/secant/Dependencies/DatabaseFiles/DatabaseFilesMocks.swift deleted file mode 100644 index cc96dd6c..00000000 --- a/secant/Dependencies/DatabaseFiles/DatabaseFilesMocks.swift +++ /dev/null @@ -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 - } - ) -} diff --git a/secant/Dependencies/DatabaseFiles/DatabaseFilesTestKey.swift b/secant/Dependencies/DatabaseFiles/DatabaseFilesTestKey.swift index 5e11f1a9..543aacab 100644 --- a/secant/Dependencies/DatabaseFiles/DatabaseFilesTestKey.swift +++ b/secant/Dependencies/DatabaseFiles/DatabaseFilesTestKey.swift @@ -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 } + ) +} diff --git a/secant/Dependencies/DerivationTool/DerivationToolLiveKey.swift b/secant/Dependencies/DerivationTool/DerivationToolLiveKey.swift index 58ea86b3..a30af8d0 100644 --- a/secant/Dependencies/DerivationTool/DerivationToolLiveKey.swift +++ b/secant/Dependencies/DerivationTool/DerivationToolLiveKey.swift @@ -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) diff --git a/secant/Dependencies/DerivationTool/DerivationToolTestKey.swift b/secant/Dependencies/DerivationTool/DerivationToolTestKey.swift index e7152cc2..4d527cb0 100644 --- a/secant/Dependencies/DerivationTool/DerivationToolTestKey.swift +++ b/secant/Dependencies/DerivationTool/DerivationToolTestKey.swift @@ -43,7 +43,7 @@ extension DerivationToolClient { } extension DerivationToolClient { - static let noop = Self( + static let noOp = Self( deriveViewingKeys: { _, _ in [] }, deriveViewingKey: { _ in "" }, deriveSpendingKeys: { _, _ in [] }, diff --git a/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorInterface.swift b/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorInterface.swift new file mode 100644 index 00000000..a3723bad --- /dev/null +++ b/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorInterface.swift @@ -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 +} diff --git a/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorLiveKey.swift b/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorLiveKey.swift new file mode 100644 index 00000000..fd4e1e75 --- /dev/null +++ b/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorLiveKey.swift @@ -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) } + ) +} diff --git a/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorTestKey.swift b/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorTestKey.swift new file mode 100644 index 00000000..05a258ea --- /dev/null +++ b/secant/Dependencies/FeedbackGenerator/FeedbackGeneratorTestKey.swift @@ -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: { } + ) +} diff --git a/secant/Dependencies/FeedbackGeneratorKey.swift b/secant/Dependencies/FeedbackGeneratorKey.swift deleted file mode 100644 index 8b4e726c..00000000 --- a/secant/Dependencies/FeedbackGeneratorKey.swift +++ /dev/null @@ -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 } - } -} diff --git a/secant/Dependencies/FileManager/FileManagerInterface.swift b/secant/Dependencies/FileManager/FileManagerInterface.swift new file mode 100644 index 00000000..c81e3cf0 --- /dev/null +++ b/secant/Dependencies/FileManager/FileManagerInterface.swift @@ -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 +} diff --git a/secant/Wrappers/WrappedFileManager.swift b/secant/Dependencies/FileManager/FileManagerLive.swift similarity index 58% rename from secant/Wrappers/WrappedFileManager.swift rename to secant/Dependencies/FileManager/FileManagerLive.swift index 9474a15a..f30a7a2f 100644 --- a/secant/Wrappers/WrappedFileManager.swift +++ b/secant/Dependencies/FileManager/FileManagerLive.swift @@ -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) }, diff --git a/secant/Dependencies/FileManager/FileManagerTest.swift b/secant/Dependencies/FileManager/FileManagerTest.swift new file mode 100644 index 00000000..c64afd60 --- /dev/null +++ b/secant/Dependencies/FileManager/FileManagerTest.swift @@ -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") + ) +} diff --git a/secant/Dependencies/Mnemonic/MnemonicInterface.swift b/secant/Dependencies/Mnemonic/MnemonicInterface.swift new file mode 100644 index 00000000..2166aa00 --- /dev/null +++ b/secant/Dependencies/Mnemonic/MnemonicInterface.swift @@ -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 +} diff --git a/secant/Dependencies/Mnemonic/MnemonicLiveKey.swift b/secant/Dependencies/Mnemonic/MnemonicLiveKey.swift new file mode 100644 index 00000000..495b0426 --- /dev/null +++ b/secant/Dependencies/Mnemonic/MnemonicLiveKey.swift @@ -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) + } + ) +} diff --git a/secant/Wrappers/WrappedMnemonic.swift b/secant/Dependencies/Mnemonic/MnemonicMocks.swift similarity index 53% rename from secant/Wrappers/WrappedMnemonic.swift rename to secant/Dependencies/Mnemonic/MnemonicMocks.swift index 297322a0..1c2b4b64 100644 --- a/secant/Wrappers/WrappedMnemonic.swift +++ b/secant/Dependencies/Mnemonic/MnemonicMocks.swift @@ -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 \ diff --git a/secant/Dependencies/Mnemonic/MnemonicTestKey.swift b/secant/Dependencies/Mnemonic/MnemonicTestKey.swift new file mode 100644 index 00000000..3330d021 --- /dev/null +++ b/secant/Dependencies/Mnemonic/MnemonicTestKey.swift @@ -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 } + ) +} diff --git a/secant/Dependencies/MnemonicKey.swift b/secant/Dependencies/MnemonicKey.swift deleted file mode 100644 index 14792821..00000000 --- a/secant/Dependencies/MnemonicKey.swift +++ /dev/null @@ -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 } - } -} diff --git a/secant/Dependencies/NewRecoveryPhraseKey.swift b/secant/Dependencies/NewRecoveryPhraseKey.swift deleted file mode 100644 index dc1c46de..00000000 --- a/secant/Dependencies/NewRecoveryPhraseKey.swift +++ /dev/null @@ -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 } - } -} diff --git a/secant/Dependencies/NotificationCenter/NotificationCenterInterface.swift b/secant/Dependencies/NotificationCenter/NotificationCenterInterface.swift new file mode 100644 index 00000000..7fa0b57f --- /dev/null +++ b/secant/Dependencies/NotificationCenter/NotificationCenterInterface.swift @@ -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? +} diff --git a/secant/Dependencies/NotificationCenter/NotificationCenterLive.swift b/secant/Dependencies/NotificationCenter/NotificationCenterLive.swift new file mode 100644 index 00000000..81c4f367 --- /dev/null +++ b/secant/Dependencies/NotificationCenter/NotificationCenterLive.swift @@ -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) } + ) +} diff --git a/secant/Dependencies/NotificationCenter/NotificationCenterTest.swift b/secant/Dependencies/NotificationCenter/NotificationCenterTest.swift new file mode 100644 index 00000000..82ea62d6 --- /dev/null +++ b/secant/Dependencies/NotificationCenter/NotificationCenterTest.swift @@ -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 } + ) +} diff --git a/secant/Dependencies/NumberFormatter/NumberFormatterInterface.swift b/secant/Dependencies/NumberFormatter/NumberFormatterInterface.swift new file mode 100644 index 00000000..2a8f9868 --- /dev/null +++ b/secant/Dependencies/NumberFormatter/NumberFormatterInterface.swift @@ -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? +} diff --git a/secant/Wrappers/WrappedNumberFormatter.swift b/secant/Dependencies/NumberFormatter/NumberFormatterLiveKey.swift similarity index 80% rename from secant/Wrappers/WrappedNumberFormatter.swift rename to secant/Dependencies/NumberFormatter/NumberFormatterLiveKey.swift index 1d4ec6c0..fb15c99a 100644 --- a/secant/Wrappers/WrappedNumberFormatter.swift +++ b/secant/Dependencies/NumberFormatter/NumberFormatterLiveKey.swift @@ -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) }, diff --git a/secant/Dependencies/NumberFormatter/NumberFormatterTestKey.swift b/secant/Dependencies/NumberFormatter/NumberFormatterTestKey.swift new file mode 100644 index 00000000..fb239b28 --- /dev/null +++ b/secant/Dependencies/NumberFormatter/NumberFormatterTestKey.swift @@ -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 } + ) +} diff --git a/secant/Dependencies/NumberFormatterKey.swift b/secant/Dependencies/NumberFormatterKey.swift deleted file mode 100644 index 52ec31c6..00000000 --- a/secant/Dependencies/NumberFormatterKey.swift +++ /dev/null @@ -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 } - } -} diff --git a/secant/Dependencies/Pasteboard/PasteboardInterface.swift b/secant/Dependencies/Pasteboard/PasteboardInterface.swift new file mode 100644 index 00000000..ab73bebc --- /dev/null +++ b/secant/Dependencies/Pasteboard/PasteboardInterface.swift @@ -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? +} diff --git a/secant/Dependencies/Pasteboard/PasteboardLiveKey.swift b/secant/Dependencies/Pasteboard/PasteboardLiveKey.swift new file mode 100644 index 00000000..1ab8a1f8 --- /dev/null +++ b/secant/Dependencies/Pasteboard/PasteboardLiveKey.swift @@ -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 } + ) +} diff --git a/secant/Dependencies/Pasteboard/PasteboardTestKey.swift b/secant/Dependencies/Pasteboard/PasteboardTestKey.swift new file mode 100644 index 00000000..c5aa7271 --- /dev/null +++ b/secant/Dependencies/Pasteboard/PasteboardTestKey.swift @@ -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 } + ) +} diff --git a/secant/Dependencies/PasteboardKey.swift b/secant/Dependencies/PasteboardKey.swift deleted file mode 100644 index c5321b12..00000000 --- a/secant/Dependencies/PasteboardKey.swift +++ /dev/null @@ -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 } - } -} diff --git a/secant/Dependencies/RecoveryPhraseRandomizer.swift b/secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizer.swift similarity index 60% rename from secant/Dependencies/RecoveryPhraseRandomizer.swift rename to secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizer.swift index 4b0f6b5b..d80ff0d3 100644 --- a/secant/Dependencies/RecoveryPhraseRandomizer.swift +++ b/secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizer.swift @@ -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 +} diff --git a/secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizerLiveKey.swift b/secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizerLiveKey.swift new file mode 100644 index 00000000..42b0f539 --- /dev/null +++ b/secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizerLiveKey.swift @@ -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) } + ) +} diff --git a/secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizerTestKey.swift b/secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizerTestKey.swift new file mode 100644 index 00000000..5d85fb0d --- /dev/null +++ b/secant/Dependencies/RecoveryPhraseRandomizer/RecoveryPhraseRandomizerTestKey.swift @@ -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) + ) +} diff --git a/secant/Dependencies/SDKSynchronizer/SDKSynchronizerInterface.swift b/secant/Dependencies/SDKSynchronizer/SDKSynchronizerInterface.swift new file mode 100644 index 00000000..ed1adb6e --- /dev/null +++ b/secant/Dependencies/SDKSynchronizer/SDKSynchronizerInterface.swift @@ -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 { 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, Never> +} + +extension SDKSynchronizerClient { + func start() throws { + try start(retry: false) + } + + func getTransparentAddress() -> TransparentAddress? { + getTransparentAddress(account: 0) + } + + func getShieldedAddress() -> SaplingShieldedAddress? { + getShieldedAddress(account: 0) + } +} diff --git a/secant/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift b/secant/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift new file mode 100644 index 00000000..bbf7b309 --- /dev/null +++ b/secant/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift @@ -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 + 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(.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, 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() + } +} diff --git a/secant/Dependencies/SDKSynchronizer/SDKSynchronizerMocks.swift b/secant/Dependencies/SDKSynchronizer/SDKSynchronizerMocks.swift new file mode 100644 index 00000000..3a814131 --- /dev/null +++ b/secant/Dependencies/SDKSynchronizer/SDKSynchronizerMocks.swift @@ -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 + private(set) var walletBirthday: BlockHeight? + private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState? + + init(notificationCenter: NotificationCenterClient = .noOp) { + self.notificationCenter = notificationCenter + self.stateChanged = CurrentValueSubject(.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, 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)) + } +} diff --git a/secant/Dependencies/SDKSynchronizer/SDKSynchronizerTest.swift b/secant/Dependencies/SDKSynchronizer/SDKSynchronizerTest.swift new file mode 100644 index 00000000..bcb7513d --- /dev/null +++ b/secant/Dependencies/SDKSynchronizer/SDKSynchronizerTest.swift @@ -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 + private(set) var walletBirthday: BlockHeight? + private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState? + + init(notificationCenter: NotificationCenterClient = .noOp) { + self.notificationCenter = notificationCenter + self.stateChanged = CurrentValueSubject(.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, Never> { + Effect(value: Result.failure(SynchronizerError.criticalError as NSError)) + } + + func updateStateChanged(_ newState: SDKSynchronizerState) { + stateChanged = CurrentValueSubject(newState) + } +} + +class T2estSDKSynchronizerClient: SDKSynchronizerClient { + private(set) var blockProcessor: CompactBlockProcessor? + private(set) var notificationCenter: NotificationCenterClient + private(set) var synchronizer: SDKSynchronizer? + private(set) var stateChanged: CurrentValueSubject + private(set) var walletBirthday: BlockHeight? + private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState? + + init(notificationCenter: NotificationCenterClient = .noOp) { + self.notificationCenter = notificationCenter + self.stateChanged = CurrentValueSubject(.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, Never> { + return Effect(value: Result.failure(SynchronizerError.criticalError as NSError)) + } + + func updateStateChanged(_ newState: SDKSynchronizerState) { + stateChanged = CurrentValueSubject(newState) + } +} diff --git a/secant/Dependencies/SDKSynchronizerKey.swift b/secant/Dependencies/SDKSynchronizerKey.swift deleted file mode 100644 index d3a06c44..00000000 --- a/secant/Dependencies/SDKSynchronizerKey.swift +++ /dev/null @@ -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 } - } -} diff --git a/secant/Dependencies/SecItem/SecItemInterface.swift b/secant/Dependencies/SecItem/SecItemInterface.swift new file mode 100644 index 00000000..d3ceef69 --- /dev/null +++ b/secant/Dependencies/SecItem/SecItemInterface.swift @@ -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 +} diff --git a/secant/Wrappers/WrappedSecItem.swift b/secant/Dependencies/SecItem/SecItemLive.swift similarity index 53% rename from secant/Wrappers/WrappedSecItem.swift rename to secant/Dependencies/SecItem/SecItemLive.swift index 4a87e387..618a5db0 100644 --- a/secant/Wrappers/WrappedSecItem.swift +++ b/secant/Dependencies/SecItem/SecItemLive.swift @@ -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) }, diff --git a/secant/Dependencies/URIParser.swift b/secant/Dependencies/URIParser/URIParser.swift similarity index 56% rename from secant/Dependencies/URIParser.swift rename to secant/Dependencies/URIParser/URIParser.swift index 46e54e75..9c266408 100644 --- a/secant/Dependencies/URIParser.swift +++ b/secant/Dependencies/URIParser/URIParser.swift @@ -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 } - } -} diff --git a/secant/Dependencies/URIParser/URIParserInterface.swift b/secant/Dependencies/URIParser/URIParserInterface.swift new file mode 100644 index 00000000..5374926f --- /dev/null +++ b/secant/Dependencies/URIParser/URIParserInterface.swift @@ -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 +} + diff --git a/secant/Wrappers/WrappedURIParser.swift b/secant/Dependencies/URIParser/URIParserLive.swift similarity index 55% rename from secant/Wrappers/WrappedURIParser.swift rename to secant/Dependencies/URIParser/URIParserLive.swift index ac4e2ab7..0dc6710d 100644 --- a/secant/Wrappers/WrappedURIParser.swift +++ b/secant/Dependencies/URIParser/URIParserLive.swift @@ -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 { ) } } - diff --git a/secant/Dependencies/URIParser/URIParserTest.swift b/secant/Dependencies/URIParser/URIParserTest.swift new file mode 100644 index 00000000..dd56b029 --- /dev/null +++ b/secant/Dependencies/URIParser/URIParserTest.swift @@ -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) + ) +} diff --git a/secant/Dependencies/UserDefaults/UserDefaultsInterface.swift b/secant/Dependencies/UserDefaults/UserDefaultsInterface.swift new file mode 100644 index 00000000..0c17f67c --- /dev/null +++ b/secant/Dependencies/UserDefaults/UserDefaultsInterface.swift @@ -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 +} diff --git a/secant/Dependencies/UserDefaults/UserDefaultsLiveKey.swift b/secant/Dependencies/UserDefaults/UserDefaultsLiveKey.swift new file mode 100644 index 00000000..f411d61c --- /dev/null +++ b/secant/Dependencies/UserDefaults/UserDefaultsLiveKey.swift @@ -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() } + ) + } +} diff --git a/secant/Dependencies/UserDefaults/UserDefaultsTestKey.swift b/secant/Dependencies/UserDefaults/UserDefaultsTestKey.swift new file mode 100644 index 00000000..f1a44667 --- /dev/null +++ b/secant/Dependencies/UserDefaults/UserDefaultsTestKey.swift @@ -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 } + ) +} diff --git a/secant/Dependencies/UserPreferencesStorage.swift b/secant/Dependencies/UserPreferencesStorage/UserPreferencesStorage.swift similarity index 84% rename from secant/Dependencies/UserPreferencesStorage.swift rename to secant/Dependencies/UserPreferencesStorage/UserPreferencesStorage.swift index 6bdaaa40..ac319749 100644 --- a/secant/Dependencies/UserPreferencesStorage.swift +++ b/secant/Dependencies/UserPreferencesStorage/UserPreferencesStorage.swift @@ -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 - ) -} diff --git a/secant/Dependencies/UserPreferencesStorage/UserPreferencesStorageLive.swift b/secant/Dependencies/UserPreferencesStorage/UserPreferencesStorageLive.swift new file mode 100644 index 00000000..5bd5a90f --- /dev/null +++ b/secant/Dependencies/UserPreferencesStorage/UserPreferencesStorageLive.swift @@ -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() + ) +} diff --git a/secant/Dependencies/UserPreferencesStorage/UserPreferencesStorageMocks.swift b/secant/Dependencies/UserPreferencesStorage/UserPreferencesStorageMocks.swift new file mode 100644 index 00000000..0b5d38e8 --- /dev/null +++ b/secant/Dependencies/UserPreferencesStorage/UserPreferencesStorageMocks.swift @@ -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 + ) +} diff --git a/secant/Dependencies/WalletStorage.swift b/secant/Dependencies/WalletStorage/WalletStorage.swift similarity index 99% rename from secant/Dependencies/WalletStorage.swift rename to secant/Dependencies/WalletStorage/WalletStorage.swift index b2fb911d..678d4234 100644 --- a/secant/Dependencies/WalletStorage.swift +++ b/secant/Dependencies/WalletStorage/WalletStorage.swift @@ -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 } diff --git a/secant/Wrappers/WrappedWalletStorage.swift b/secant/Dependencies/WalletStorage/WalletStorageInterface.swift similarity index 56% rename from secant/Wrappers/WrappedWalletStorage.swift rename to secant/Dependencies/WalletStorage/WalletStorageInterface.swift index 691460d1..ae01f4bd 100644 --- a/secant/Wrappers/WrappedWalletStorage.swift +++ b/secant/Dependencies/WalletStorage/WalletStorageInterface.swift @@ -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: { } - ) -} diff --git a/secant/Dependencies/WalletStorage/WalletStorageLiveKey.swift b/secant/Dependencies/WalletStorage/WalletStorageLiveKey.swift new file mode 100644 index 00000000..7d4d3920 --- /dev/null +++ b/secant/Dependencies/WalletStorage/WalletStorageLiveKey.swift @@ -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() + } + ) + } +} diff --git a/secant/Dependencies/WalletStorage/WalletStorageTestKey.swift b/secant/Dependencies/WalletStorage/WalletStorageTestKey.swift new file mode 100644 index 00000000..db87032a --- /dev/null +++ b/secant/Dependencies/WalletStorage/WalletStorageTestKey.swift @@ -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: { } + ) +} diff --git a/secant/Dependencies/WalletStorageKey.swift b/secant/Dependencies/WalletStorageKey.swift deleted file mode 100644 index 2e372b3a..00000000 --- a/secant/Dependencies/WalletStorageKey.swift +++ /dev/null @@ -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 } - } -} diff --git a/secant/Dependencies/ZCashSDKEnvironment.swift b/secant/Dependencies/ZCashSDKEnvironment.swift deleted file mode 100644 index c0931774..00000000 --- a/secant/Dependencies/ZCashSDKEnvironment.swift +++ /dev/null @@ -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 } - } -} diff --git a/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentInterface.swift b/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentInterface.swift new file mode 100644 index 00000000..e9d6a4d7 --- /dev/null +++ b/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentInterface.swift @@ -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 +} diff --git a/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentLiveKey.swift b/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentLiveKey.swift new file mode 100644 index 00000000..25e4de79 --- /dev/null +++ b/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentLiveKey.swift @@ -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" + ) +} diff --git a/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentTestKey.swift b/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentTestKey.swift new file mode 100644 index 00000000..546055f4 --- /dev/null +++ b/secant/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentTestKey.swift @@ -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" + ) +} diff --git a/secant/Features/App/AppStore.swift b/secant/Features/App/AppStore.swift index 96c3d86c..235e7a58 100644 --- a/secant/Features/App/AppStore.swift +++ b/secant/Features/App/AppStore.swift @@ -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) diff --git a/secant/Features/BalanceBreakdown/BalanceBreakdownStore.swift b/secant/Features/BalanceBreakdown/BalanceBreakdownStore.swift index 2f0302a9..7d288c7d 100644 --- a/secant/Features/BalanceBreakdown/BalanceBreakdownStore.swift +++ b/secant/Features/BalanceBreakdown/BalanceBreakdownStore.swift @@ -28,7 +28,7 @@ struct BalanceBreakdownReducer: ReducerProtocol { enum Action: Equatable { case onAppear case onDisappear - case synchronizerStateChanged(WrappedSDKSynchronizerState) + case synchronizerStateChanged(SDKSynchronizerState) case updateLatestBlock case updateSynchronizerStatus } diff --git a/secant/Features/Home/HomeStore.swift b/secant/Features/Home/HomeStore.swift index d2138ee1..bf255273 100644 --- a/secant/Features/Home/HomeStore.swift +++ b/secant/Features/Home/HomeStore.swift @@ -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?) diff --git a/secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayStore.swift b/secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayStore.swift index 6118da93..7a124750 100644 --- a/secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayStore.swift +++ b/secant/Features/RecoveryPhraseDisplay/RecoveryPhraseDisplayStore.swift @@ -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 { 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) diff --git a/secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowStore.swift b/secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowStore.swift index 8837aaea..645eed0a 100644 --- a/secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowStore.swift +++ b/secant/Features/RecoveryPhraseValidationFlow/RecoveryPhraseValidationFlowStore.swift @@ -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 { 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 diff --git a/secant/Features/SendFlow/SendFlowStore.swift b/secant/Features/SendFlow/SendFlowStore.swift index 744decdf..79249ff0 100644 --- a/secant/Features/SendFlow/SendFlowStore.swift +++ b/secant/Features/SendFlow/SendFlowStore.swift @@ -80,7 +80,7 @@ struct SendFlowReducer: ReducerProtocol { case onDisappear case sendConfirmationPressed case sendTransactionResult(Result) - case synchronizerStateChanged(WrappedSDKSynchronizerState) + case synchronizerStateChanged(SDKSynchronizerState) case transactionAddressInput(TransactionAddressTextFieldReducer.Action) case transactionAmountInput(TransactionAmountTextFieldReducer.Action) case updateRoute(SendFlowReducer.State.Route?) diff --git a/secant/Features/WalletEventsFlow/WalletEventsFlowStore.swift b/secant/Features/WalletEventsFlow/WalletEventsFlowStore.swift index c050e33e..5caff695 100644 --- a/secant/Features/WalletEventsFlow/WalletEventsFlowStore.swift +++ b/secant/Features/WalletEventsFlow/WalletEventsFlowStore.swift @@ -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?) } diff --git a/secant/Models/StoredWallet.swift b/secant/Models/StoredWallet.swift index 69fac12f..3f393fb0 100644 --- a/secant/Models/StoredWallet.swift +++ b/secant/Models/StoredWallet.swift @@ -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 + ) +} diff --git a/secant/Wrappers/WrappedFeedbackGenerator.swift b/secant/Wrappers/WrappedFeedbackGenerator.swift deleted file mode 100644 index b4e05710..00000000 --- a/secant/Wrappers/WrappedFeedbackGenerator.swift +++ /dev/null @@ -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: { } - ) -} diff --git a/secant/Wrappers/WrappedNotificationCenter.swift b/secant/Wrappers/WrappedNotificationCenter.swift deleted file mode 100644 index 044df2b3..00000000 --- a/secant/Wrappers/WrappedNotificationCenter.swift +++ /dev/null @@ -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 } - ) -} diff --git a/secant/Wrappers/WrappedPasteboard.swift b/secant/Wrappers/WrappedPasteboard.swift deleted file mode 100644 index e260f1b5..00000000 --- a/secant/Wrappers/WrappedPasteboard.swift +++ /dev/null @@ -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 } - ) -} diff --git a/secant/Wrappers/WrappedRecoveryPhraseRandomizer.swift b/secant/Wrappers/WrappedRecoveryPhraseRandomizer.swift deleted file mode 100644 index 915b9b89..00000000 --- a/secant/Wrappers/WrappedRecoveryPhraseRandomizer.swift +++ /dev/null @@ -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) } - ) -} diff --git a/secant/Wrappers/WrappedSDKSynchronizer.swift b/secant/Wrappers/WrappedSDKSynchronizer.swift deleted file mode 100644 index 8331853d..00000000 --- a/secant/Wrappers/WrappedSDKSynchronizer.swift +++ /dev/null @@ -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 { 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, 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 - 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(.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, 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 - private(set) var walletBirthday: BlockHeight? - private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState? - - init(notificationCenter: WrappedNotificationCenter = .mock) { - self.notificationCenter = notificationCenter - self.stateChanged = CurrentValueSubject(.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, 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 - private(set) var walletBirthday: BlockHeight? - private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState? - - init(notificationCenter: WrappedNotificationCenter = .mock) { - self.notificationCenter = notificationCenter - self.stateChanged = CurrentValueSubject(.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, Never> { - return Effect(value: Result.failure(SynchronizerError.criticalError as NSError)) - } - - func updateStateChanged(_ newState: WrappedSDKSynchronizerState) { - stateChanged = CurrentValueSubject(newState) - } -} diff --git a/secant/Wrappers/WrappedUserDefaults.swift b/secant/Wrappers/WrappedUserDefaults.swift deleted file mode 100644 index bba91580..00000000 --- a/secant/Wrappers/WrappedUserDefaults.swift +++ /dev/null @@ -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 } - ) -} diff --git a/secantTests/AppTests/AppInitializationTests.swift b/secantTests/AppTests/AppInitializationTests.swift index 229b3017..ce295052 100644 --- a/secantTests/AppTests/AppInitializationTests.swift +++ b/secantTests/AppTests/AppInitializationTests.swift @@ -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)) diff --git a/secantTests/AppTests/AppTests.swift b/secantTests/AppTests/AppTests.swift index 98c02045..219ddf51 100644 --- a/secantTests/AppTests/AppTests.swift +++ b/secantTests/AppTests/AppTests.swift @@ -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 diff --git a/secantTests/BackupFlowTests/RecoveryPhraseDisplayReducerTests.swift b/secantTests/BackupFlowTests/RecoveryPhraseDisplayReducerTests.swift index 7257e565..a1ca3e76 100644 --- a/secantTests/BackupFlowTests/RecoveryPhraseDisplayReducerTests.swift +++ b/secantTests/BackupFlowTests/RecoveryPhraseDisplayReducerTests.swift @@ -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 diff --git a/secantTests/BalanceBreakdownTests/BalanceBreakdownTests.swift b/secantTests/BalanceBreakdownTests/BalanceBreakdownTests.swift index 888be4fb..5ec64d2c 100644 --- a/secantTests/BalanceBreakdownTests/BalanceBreakdownTests.swift +++ b/secantTests/BalanceBreakdownTests/BalanceBreakdownTests.swift @@ -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) diff --git a/secantTests/DeeplinkTests/DeeplinkTests.swift b/secantTests/DeeplinkTests/DeeplinkTests.swift index 25f92ed4..47796cbc 100644 --- a/secantTests/DeeplinkTests/DeeplinkTests.swift +++ b/secantTests/DeeplinkTests/DeeplinkTests.swift @@ -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)) diff --git a/secantTests/HomeTests/HomeTests.swift b/secantTests/HomeTests/HomeTests.swift index a795b5c9..c9f3831b 100644 --- a/secantTests/HomeTests/HomeTests.swift +++ b/secantTests/HomeTests/HomeTests.swift @@ -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( diff --git a/secantTests/ImportWalletTests/ImportWalletTests.swift b/secantTests/ImportWalletTests/ImportWalletTests.swift index 3f56d570..cd0ca94a 100644 --- a/secantTests/ImportWalletTests/ImportWalletTests.swift +++ b/secantTests/ImportWalletTests/ImportWalletTests.swift @@ -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"), diff --git a/secantTests/ProfileTests/ProfileTests.swift b/secantTests/ProfileTests/ProfileTests.swift index a56333ca..147713fe 100644 --- a/secantTests/ProfileTests/ProfileTests.swift +++ b/secantTests/ProfileTests/ProfileTests.swift @@ -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" diff --git a/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationTests.swift b/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationTests.swift index cfc71dde..3dc429e8 100644 --- a/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationTests.swift +++ b/secantTests/RecoveryPhraseValidationTests/RecoveryPhraseValidationTests.swift @@ -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 diff --git a/secantTests/ScanTests/ScanTests.swift b/secantTests/ScanTests/ScanTests.swift index 148420d0..ebd62094 100644 --- a/secantTests/ScanTests/ScanTests.swift +++ b/secantTests/ScanTests/ScanTests.swift @@ -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 diff --git a/secantTests/SendTests/SendTests.swift b/secantTests/SendTests/SendTests.swift index 75cda2d7..3653069f 100644 --- a/secantTests/SendTests/SendTests.swift +++ b/secantTests/SendTests/SendTests.swift @@ -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 + _ 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.") } } } diff --git a/secantTests/SendTests/TransactionAmountInputTests.swift b/secantTests/SendTests/TransactionAmountInputTests.swift index de84cb8f..8022eb7e 100644 --- a/secantTests/SendTests/TransactionAmountInputTests.swift +++ b/secantTests/SendTests/TransactionAmountInputTests.swift @@ -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" } diff --git a/secantTests/SettingsTests/SettingsTests.swift b/secantTests/SettingsTests/SettingsTests.swift index 60659815..e7b334e9 100644 --- a/secantTests/SettingsTests/SettingsTests.swift +++ b/secantTests/SettingsTests/SettingsTests.swift @@ -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 diff --git a/secantTests/SnapshotTests/HomeSnapshotTests/HomeCircularProgressSnapshotTests.swift b/secantTests/SnapshotTests/HomeSnapshotTests/HomeCircularProgressSnapshotTests.swift index 15dcd523..cb635184 100644 --- a/secantTests/SnapshotTests/HomeSnapshotTests/HomeCircularProgressSnapshotTests.swift +++ b/secantTests/SnapshotTests/HomeSnapshotTests/HomeCircularProgressSnapshotTests.swift @@ -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) } diff --git a/secantTests/SnapshotTests/ProfileSnapshotTests/ProfileSnapshotTests.swift b/secantTests/SnapshotTests/ProfileSnapshotTests/ProfileSnapshotTests.swift index 96808ff9..885fea9c 100644 --- a/secantTests/SnapshotTests/ProfileSnapshotTests/ProfileSnapshotTests.swift +++ b/secantTests/SnapshotTests/ProfileSnapshotTests/ProfileSnapshotTests.swift @@ -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) diff --git a/secantTests/SnapshotTests/RecoveryPhraseValidationFlowSnapshotTests/RecoveryPhraseValidationFlowSnapshotTests.swift b/secantTests/SnapshotTests/RecoveryPhraseValidationFlowSnapshotTests/RecoveryPhraseValidationFlowSnapshotTests.swift index 6654e3f0..9b27214b 100644 --- a/secantTests/SnapshotTests/RecoveryPhraseValidationFlowSnapshotTests/RecoveryPhraseValidationFlowSnapshotTests.swift +++ b/secantTests/SnapshotTests/RecoveryPhraseValidationFlowSnapshotTests/RecoveryPhraseValidationFlowSnapshotTests.swift @@ -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) diff --git a/secantTests/SnapshotTests/SendSnapshotTests/TransactionConfirmationSnapshotTests.swift b/secantTests/SnapshotTests/SendSnapshotTests/TransactionConfirmationSnapshotTests.swift index bfcffbe9..4f904f0f 100644 --- a/secantTests/SnapshotTests/SendSnapshotTests/TransactionConfirmationSnapshotTests.swift +++ b/secantTests/SnapshotTests/SendSnapshotTests/TransactionConfirmationSnapshotTests.swift @@ -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) diff --git a/secantTests/SnapshotTests/SendSnapshotTests/TransactionSendingTests.swift b/secantTests/SnapshotTests/SendSnapshotTests/TransactionSendingSnapshotTests.swift similarity index 97% rename from secantTests/SnapshotTests/SendSnapshotTests/TransactionSendingTests.swift rename to secantTests/SnapshotTests/SendSnapshotTests/TransactionSendingSnapshotTests.swift index 3cb6a0fb..2468e91e 100644 --- a/secantTests/SnapshotTests/SendSnapshotTests/TransactionSendingTests.swift +++ b/secantTests/SnapshotTests/SendSnapshotTests/TransactionSendingSnapshotTests.swift @@ -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) diff --git a/secantTests/SnapshotTests/SettingsSnapshotTests/SettingsSnapshotTests.swift b/secantTests/SnapshotTests/SettingsSnapshotTests/SettingsSnapshotTests.swift index f68b8de1..46192253 100644 --- a/secantTests/SnapshotTests/SettingsSnapshotTests/SettingsSnapshotTests.swift +++ b/secantTests/SnapshotTests/SettingsSnapshotTests/SettingsSnapshotTests.swift @@ -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)) diff --git a/secantTests/UtilTests/DatabaseFilesTests.swift b/secantTests/UtilTests/DatabaseFilesTests.swift index 4b486c18..652aec78 100644 --- a/secantTests/UtilTests/DatabaseFilesTests.swift +++ b/secantTests/UtilTests/DatabaseFilesTests.swift @@ -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 } diff --git a/secantTests/UtilTests/WrappedSecItemTests.swift b/secantTests/UtilTests/SecItemClientTests.swift similarity index 73% rename from secantTests/UtilTests/WrappedSecItemTests.swift rename to secantTests/UtilTests/SecItemClientTests.swift index 354abcd2..b2952c1b 100644 --- a/secantTests/UtilTests/WrappedSecItemTests.swift +++ b/secantTests/UtilTests/SecItemClientTests.swift @@ -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 }, diff --git a/secantTests/UtilTests/UserPreferencesStorageTests.swift b/secantTests/UtilTests/UserPreferencesStorageTests.swift index 3d60c857..6b666b8d 100644 --- a/secantTests/UtilTests/UserPreferencesStorageTests.swift +++ b/secantTests/UtilTests/UserPreferencesStorageTests.swift @@ -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 }, diff --git a/secantTests/WalletEventsTests/WalletEventsTests.swift b/secantTests/WalletEventsTests/WalletEventsTests.swift index e990503c..3189dcad 100644 --- a/secantTests/WalletEventsTests/WalletEventsTests.swift +++ b/secantTests/WalletEventsTests/WalletEventsTests.swift @@ -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)`" + ) } }