- OSLogger for the defined categories - TCA logger for the TCA logs - WalletLogger for the secant logs - SDKLogger passed to the SDK - unit tests for the loggers - export category OS logs - share txt files (sdk, tca, wallet logs) via native share dialog - timestamp extension so we see even milliseconds - txt files up to some X size - simple button enable/disable logic and wrapping the export work in the Task - TODO for empty catches - OSLogger refactored to OSLogger_, just temporary change - export and share divided into business logic and view logic parts - unit tests for the TCA part - async let syntax for the export logs - simple activity indicator so testers know export is in progress - static date formatters so we don't instantiate it over and over
This commit is contained in:
parent
0d2d898f4e
commit
1aca887800
|
@ -99,6 +99,11 @@
|
||||||
9E01F8282833CDA0000EFC57 /* ScanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E01F8272833CDA0000EFC57 /* ScanTests.swift */; };
|
9E01F8282833CDA0000EFC57 /* ScanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E01F8272833CDA0000EFC57 /* ScanTests.swift */; };
|
||||||
9E02B56A27FED43E005B809B /* FileManagerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56927FED43E005B809B /* FileManagerInterface.swift */; };
|
9E02B56A27FED43E005B809B /* FileManagerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56927FED43E005B809B /* FileManagerInterface.swift */; };
|
||||||
9E02B56C27FED475005B809B /* DatabaseFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */; };
|
9E02B56C27FED475005B809B /* DatabaseFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */; };
|
||||||
|
9E0F5741297E7F1D005304FA /* TCALogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0F5740297E7F1C005304FA /* TCALogger.swift */; };
|
||||||
|
9E0F5743297EB96C005304FA /* TCALoggerReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0F5742297EB96C005304FA /* TCALoggerReducer.swift */; };
|
||||||
|
9E0F5745297EBA1B005304FA /* LogStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0F5744297EBA1B005304FA /* LogStore.swift */; };
|
||||||
|
9E0F5747297EE5F3005304FA /* OSLogger_.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0F5746297EE5F3005304FA /* OSLogger_.swift */; };
|
||||||
|
9E0F574B2980260D005304FA /* LoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0F574A2980260D005304FA /* LoggerTests.swift */; };
|
||||||
9E153A5F2920CE2700112F41 /* MnemonicInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5E2920CD5100112F41 /* MnemonicInterface.swift */; };
|
9E153A5F2920CE2700112F41 /* MnemonicInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5E2920CD5100112F41 /* MnemonicInterface.swift */; };
|
||||||
9E153A602920CE2700112F41 /* MnemonicLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5C2920CD5100112F41 /* MnemonicLiveKey.swift */; };
|
9E153A602920CE2700112F41 /* MnemonicLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5C2920CD5100112F41 /* MnemonicLiveKey.swift */; };
|
||||||
9E153A612920CE2700112F41 /* MnemonicMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5B2920CD5100112F41 /* MnemonicMocks.swift */; };
|
9E153A612920CE2700112F41 /* MnemonicMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A5B2920CD5100112F41 /* MnemonicMocks.swift */; };
|
||||||
|
@ -137,6 +142,10 @@
|
||||||
9E5BF648282277BE00BA3F17 /* NotificationCenterInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */; };
|
9E5BF648282277BE00BA3F17 /* NotificationCenterInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */; };
|
||||||
9E5BF64F2823E94900BA3F17 /* TransactionAddressTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */; };
|
9E5BF64F2823E94900BA3F17 /* TransactionAddressTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */; };
|
||||||
9E5BF6502823E94900BA3F17 /* TransactionAddressTextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */; };
|
9E5BF6502823E94900BA3F17 /* TransactionAddressTextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */; };
|
||||||
|
9E612C6F2987A9B100D09B09 /* UIShareDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E612C6E2987A9B100D09B09 /* UIShareDialog.swift */; };
|
||||||
|
9E612C7229880E9200D09B09 /* LogsHandlerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E612C7129880E9200D09B09 /* LogsHandlerInterface.swift */; };
|
||||||
|
9E612C7429880F2200D09B09 /* LogsHandlerLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E612C7329880F2200D09B09 /* LogsHandlerLive.swift */; };
|
||||||
|
9E612C7629880FC900D09B09 /* LogsHandlerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E612C7529880FC900D09B09 /* LogsHandlerTest.swift */; };
|
||||||
9E66122A287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E661229287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift */; };
|
9E66122A287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E661229287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift */; };
|
||||||
9E66122C2877188700C75B70 /* SyncStatusSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E66122B2877188700C75B70 /* SyncStatusSnapshot.swift */; };
|
9E66122C2877188700C75B70 /* SyncStatusSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E66122B2877188700C75B70 /* SyncStatusSnapshot.swift */; };
|
||||||
9E6612312878337F00C75B70 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 9E6612302878337F00C75B70 /* Lottie */; };
|
9E6612312878337F00C75B70 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 9E6612302878337F00C75B70 /* Lottie */; };
|
||||||
|
@ -395,6 +404,11 @@
|
||||||
9E01F8272833CDA0000EFC57 /* ScanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanTests.swift; sourceTree = "<group>"; };
|
9E01F8272833CDA0000EFC57 /* ScanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanTests.swift; sourceTree = "<group>"; };
|
||||||
9E02B56927FED43E005B809B /* FileManagerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerInterface.swift; sourceTree = "<group>"; };
|
9E02B56927FED43E005B809B /* FileManagerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerInterface.swift; sourceTree = "<group>"; };
|
||||||
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFilesTests.swift; sourceTree = "<group>"; };
|
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFilesTests.swift; sourceTree = "<group>"; };
|
||||||
|
9E0F5740297E7F1C005304FA /* TCALogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCALogger.swift; sourceTree = "<group>"; };
|
||||||
|
9E0F5742297EB96C005304FA /* TCALoggerReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCALoggerReducer.swift; sourceTree = "<group>"; };
|
||||||
|
9E0F5744297EBA1B005304FA /* LogStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogStore.swift; sourceTree = "<group>"; };
|
||||||
|
9E0F5746297EE5F3005304FA /* OSLogger_.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSLogger_.swift; sourceTree = "<group>"; };
|
||||||
|
9E0F574A2980260D005304FA /* LoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggerTests.swift; sourceTree = "<group>"; };
|
||||||
9E153A5B2920CD5100112F41 /* MnemonicMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicMocks.swift; sourceTree = "<group>"; };
|
9E153A5B2920CD5100112F41 /* MnemonicMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicMocks.swift; sourceTree = "<group>"; };
|
||||||
9E153A5C2920CD5100112F41 /* MnemonicLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicLiveKey.swift; sourceTree = "<group>"; };
|
9E153A5C2920CD5100112F41 /* MnemonicLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicLiveKey.swift; sourceTree = "<group>"; };
|
||||||
9E153A5D2920CD5100112F41 /* MnemonicTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicTestKey.swift; sourceTree = "<group>"; };
|
9E153A5D2920CD5100112F41 /* MnemonicTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicTestKey.swift; sourceTree = "<group>"; };
|
||||||
|
@ -434,6 +448,10 @@
|
||||||
9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterInterface.swift; sourceTree = "<group>"; };
|
9E5BF647282277BE00BA3F17 /* NotificationCenterInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterInterface.swift; sourceTree = "<group>"; };
|
||||||
9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextField.swift; sourceTree = "<group>"; };
|
9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextField.swift; sourceTree = "<group>"; };
|
||||||
9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextFieldStore.swift; sourceTree = "<group>"; };
|
9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextFieldStore.swift; sourceTree = "<group>"; };
|
||||||
|
9E612C6E2987A9B100D09B09 /* UIShareDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIShareDialog.swift; sourceTree = "<group>"; };
|
||||||
|
9E612C7129880E9200D09B09 /* LogsHandlerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsHandlerInterface.swift; sourceTree = "<group>"; };
|
||||||
|
9E612C7329880F2200D09B09 /* LogsHandlerLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsHandlerLive.swift; sourceTree = "<group>"; };
|
||||||
|
9E612C7529880FC900D09B09 /* LogsHandlerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsHandlerTest.swift; sourceTree = "<group>"; };
|
||||||
9E661229287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCircularProgressSnapshotTests.swift; sourceTree = "<group>"; };
|
9E661229287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCircularProgressSnapshotTests.swift; sourceTree = "<group>"; };
|
||||||
9E66122B2877188700C75B70 /* SyncStatusSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStatusSnapshot.swift; sourceTree = "<group>"; };
|
9E66122B2877188700C75B70 /* SyncStatusSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStatusSnapshot.swift; sourceTree = "<group>"; };
|
||||||
9E6612322878338C00C75B70 /* LottieAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LottieAnimation.swift; sourceTree = "<group>"; };
|
9E6612322878338C00C75B70 /* LottieAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LottieAnimation.swift; sourceTree = "<group>"; };
|
||||||
|
@ -962,6 +980,17 @@
|
||||||
path = ScanTests;
|
path = ScanTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
9E0F573F297E7F00005304FA /* Logging */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9E0F5740297E7F1C005304FA /* TCALogger.swift */,
|
||||||
|
9E0F5742297EB96C005304FA /* TCALoggerReducer.swift */,
|
||||||
|
9E0F5744297EBA1B005304FA /* LogStore.swift */,
|
||||||
|
9E0F5746297EE5F3005304FA /* OSLogger_.swift */,
|
||||||
|
);
|
||||||
|
path = Logging;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9E153A5A2920CCE700112F41 /* Mnemonic */ = {
|
9E153A5A2920CCE700112F41 /* Mnemonic */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1109,6 +1138,24 @@
|
||||||
path = TransactionAddress;
|
path = TransactionAddress;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
9E612C6D2987A96500D09B09 /* UIKitBridge */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9E612C6E2987A9B100D09B09 /* UIShareDialog.swift */,
|
||||||
|
);
|
||||||
|
path = UIKitBridge;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9E612C7029880E6700D09B09 /* LogsHandler */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9E612C7129880E9200D09B09 /* LogsHandlerInterface.swift */,
|
||||||
|
9E612C7329880F2200D09B09 /* LogsHandlerLive.swift */,
|
||||||
|
9E612C7529880FC900D09B09 /* LogsHandlerTest.swift */,
|
||||||
|
);
|
||||||
|
path = LogsHandler;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9E6612342878341F00C75B70 /* Lotties */ = {
|
9E6612342878341F00C75B70 /* Lotties */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1226,6 +1273,7 @@
|
||||||
9E7FE0BB282D1DC200C374E8 /* Utils */ = {
|
9E7FE0BB282D1DC200C374E8 /* Utils */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
9E0F573F297E7F00005304FA /* Logging */,
|
||||||
9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */,
|
9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */,
|
||||||
F9C165B3274031F600592F76 /* Bindings.swift */,
|
F9C165B3274031F600592F76 /* Bindings.swift */,
|
||||||
0DACFA7E27208CE00039EEA5 /* Clamped.swift */,
|
0DACFA7E27208CE00039EEA5 /* Clamped.swift */,
|
||||||
|
@ -1260,6 +1308,7 @@
|
||||||
9EB863882922CC0E003D0F8B /* FeedbackGenerator */,
|
9EB863882922CC0E003D0F8B /* FeedbackGenerator */,
|
||||||
9EB863B52923C4ED003D0F8B /* FileManager */,
|
9EB863B52923C4ED003D0F8B /* FileManager */,
|
||||||
9EBDF981291F91B1000A1A05 /* LocalAuthentication */,
|
9EBDF981291F91B1000A1A05 /* LocalAuthentication */,
|
||||||
|
9E612C7029880E6700D09B09 /* LogsHandler */,
|
||||||
9E153A5A2920CCE700112F41 /* Mnemonic */,
|
9E153A5A2920CCE700112F41 /* Mnemonic */,
|
||||||
9EB863B42923C490003D0F8B /* NotificationCenter */,
|
9EB863B42923C490003D0F8B /* NotificationCenter */,
|
||||||
9EB8638F2922D000003D0F8B /* NumberFormatter */,
|
9EB8638F2922D000003D0F8B /* NumberFormatter */,
|
||||||
|
@ -1650,6 +1699,7 @@
|
||||||
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */,
|
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */,
|
||||||
9E39112D283F91600073DD9A /* ZatoshiTests.swift */,
|
9E39112D283F91600073DD9A /* ZatoshiTests.swift */,
|
||||||
0DB4E0B02881F2DB00947B78 /* WalletBalance+testing.swift */,
|
0DB4E0B02881F2DB00947B78 /* WalletBalance+testing.swift */,
|
||||||
|
9E0F574A2980260D005304FA /* LoggerTests.swift */,
|
||||||
);
|
);
|
||||||
path = UtilTests;
|
path = UtilTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1726,6 +1776,7 @@
|
||||||
children = (
|
children = (
|
||||||
F9971A6227680DFE00A2DB75 /* SettingsStore.swift */,
|
F9971A6227680DFE00A2DB75 /* SettingsStore.swift */,
|
||||||
F9971A6427680DFE00A2DB75 /* SettingsView.swift */,
|
F9971A6427680DFE00A2DB75 /* SettingsView.swift */,
|
||||||
|
9E612C6D2987A96500D09B09 /* UIKitBridge */,
|
||||||
);
|
);
|
||||||
path = Settings;
|
path = Settings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1996,6 +2047,7 @@
|
||||||
2EB7758727FC67FD00269373 /* TransactionAmountTextFieldStore.swift in Sources */,
|
2EB7758727FC67FD00269373 /* TransactionAmountTextFieldStore.swift in Sources */,
|
||||||
669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */,
|
669FDAE9272C23B3007B9422 /* CircularFrame.swift in Sources */,
|
||||||
9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */,
|
9EF8136027F043CC0075AF48 /* AppDelegate.swift in Sources */,
|
||||||
|
9E612C7229880E9200D09B09 /* LogsHandlerInterface.swift in Sources */,
|
||||||
9EBDF960291E657B000A1A05 /* DeeplinkTestKey.swift in Sources */,
|
9EBDF960291E657B000A1A05 /* DeeplinkTestKey.swift in Sources */,
|
||||||
34E0AF1128DEE5220034CF37 /* Wedge.swift in Sources */,
|
34E0AF1128DEE5220034CF37 /* Wedge.swift in Sources */,
|
||||||
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */,
|
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */,
|
||||||
|
@ -2013,9 +2065,11 @@
|
||||||
0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */,
|
0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */,
|
||||||
9EAB467A2861EA6A002904A0 /* TransactionRowView.swift in Sources */,
|
9EAB467A2861EA6A002904A0 /* TransactionRowView.swift in Sources */,
|
||||||
9EB8638C2922CD4A003D0F8B /* FeedbackGeneratorTestKey.swift in Sources */,
|
9EB8638C2922CD4A003D0F8B /* FeedbackGeneratorTestKey.swift in Sources */,
|
||||||
|
9E0F5741297E7F1D005304FA /* TCALogger.swift in Sources */,
|
||||||
0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift in Sources */,
|
0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift in Sources */,
|
||||||
9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */,
|
9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */,
|
||||||
9E9ADA7D2938F4C00071767B /* RootInitialization.swift in Sources */,
|
9E9ADA7D2938F4C00071767B /* RootInitialization.swift in Sources */,
|
||||||
|
9E612C7429880F2200D09B09 /* LogsHandlerLive.swift in Sources */,
|
||||||
9EBDF967291ECDA2000A1A05 /* AudioServicesTestKey.swift in Sources */,
|
9EBDF967291ECDA2000A1A05 /* AudioServicesTestKey.swift in Sources */,
|
||||||
0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */,
|
0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */,
|
||||||
9EBDF97E291F7EB0000A1A05 /* AppVersionInterface.swift in Sources */,
|
9EBDF97E291F7EB0000A1A05 /* AppVersionInterface.swift in Sources */,
|
||||||
|
@ -2035,6 +2089,7 @@
|
||||||
9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */,
|
9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */,
|
||||||
F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */,
|
F9971A5327680DD000A2DB75 /* ProfileStore.swift in Sources */,
|
||||||
346D41E428DF0B8600963F36 /* CheckCircle.swift in Sources */,
|
346D41E428DF0B8600963F36 /* CheckCircle.swift in Sources */,
|
||||||
|
9E0F5745297EBA1B005304FA /* LogStore.swift in Sources */,
|
||||||
9EB863AA29239EB2003D0F8B /* RecoveryPhraseRandomizer.swift in Sources */,
|
9EB863AA29239EB2003D0F8B /* RecoveryPhraseRandomizer.swift in Sources */,
|
||||||
9EB863C52923C8AF003D0F8B /* FileManagerTest.swift in Sources */,
|
9EB863C52923C8AF003D0F8B /* FileManagerTest.swift in Sources */,
|
||||||
9EB863BF2923C72C003D0F8B /* SecItemLive.swift in Sources */,
|
9EB863BF2923C72C003D0F8B /* SecItemLive.swift in Sources */,
|
||||||
|
@ -2053,6 +2108,7 @@
|
||||||
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
|
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
|
||||||
9E7FE0F92832824C00C374E8 /* QRCodeScanView.swift in Sources */,
|
9E7FE0F92832824C00C374E8 /* QRCodeScanView.swift in Sources */,
|
||||||
9E153A6F292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift in Sources */,
|
9E153A6F292167FF00112F41 /* ZcashSDKEnvironmentTestKey.swift in Sources */,
|
||||||
|
9E0F5743297EB96C005304FA /* TCALoggerReducer.swift in Sources */,
|
||||||
0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */,
|
0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */,
|
||||||
9E7225F3288AB6DD00DF7F17 /* MultipleLineTextField.swift in Sources */,
|
9E7225F3288AB6DD00DF7F17 /* MultipleLineTextField.swift in Sources */,
|
||||||
3448CB3228E47666006ADEDB /* NotEnoughFreeSpaceView.swift in Sources */,
|
3448CB3228E47666006ADEDB /* NotEnoughFreeSpaceView.swift in Sources */,
|
||||||
|
@ -2110,6 +2166,7 @@
|
||||||
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */,
|
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */,
|
||||||
663FABA0271D876200E495F8 /* PrimaryButton.swift in Sources */,
|
663FABA0271D876200E495F8 /* PrimaryButton.swift in Sources */,
|
||||||
663FAB9C271D874D00E495F8 /* ActiveButton.swift in Sources */,
|
663FAB9C271D874D00E495F8 /* ActiveButton.swift in Sources */,
|
||||||
|
9E612C6F2987A9B100D09B09 /* UIShareDialog.swift in Sources */,
|
||||||
9EBDF956291E5E86000A1A05 /* DatabaseFilesLiveKey.swift in Sources */,
|
9EBDF956291E5E86000A1A05 /* DatabaseFilesLiveKey.swift in Sources */,
|
||||||
9E2F1C842809B606004E65FE /* DebugMenu.swift in Sources */,
|
9E2F1C842809B606004E65FE /* DebugMenu.swift in Sources */,
|
||||||
9E153A5F2920CE2700112F41 /* MnemonicInterface.swift in Sources */,
|
9E153A5F2920CE2700112F41 /* MnemonicInterface.swift in Sources */,
|
||||||
|
@ -2148,6 +2205,7 @@
|
||||||
F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */,
|
F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */,
|
||||||
F96B41EB273B50520021B49A /* Strings.swift in Sources */,
|
F96B41EB273B50520021B49A /* Strings.swift in Sources */,
|
||||||
9EB863D02923D3FC003D0F8B /* SDKSynchronizerMocks.swift in Sources */,
|
9EB863D02923D3FC003D0F8B /* SDKSynchronizerMocks.swift in Sources */,
|
||||||
|
9E612C7629880FC900D09B09 /* LogsHandlerTest.swift in Sources */,
|
||||||
2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */,
|
2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */,
|
||||||
F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */,
|
F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */,
|
||||||
F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */,
|
F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */,
|
||||||
|
@ -2174,6 +2232,7 @@
|
||||||
34E5F2F328E46DB700C17E5F /* DiskSpaceChecker.swift in Sources */,
|
34E5F2F328E46DB700C17E5F /* DiskSpaceChecker.swift in Sources */,
|
||||||
F9C165C42740403600592F76 /* TransactionSentView.swift in Sources */,
|
F9C165C42740403600592F76 /* TransactionSentView.swift in Sources */,
|
||||||
9E153A6E292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift in Sources */,
|
9E153A6E292167FF00112F41 /* ZcashSDKEnvironmentLiveKey.swift in Sources */,
|
||||||
|
9E0F5747297EE5F3005304FA /* OSLogger_.swift in Sources */,
|
||||||
F9971A5927680DDE00A2DB75 /* RequestStore.swift in Sources */,
|
F9971A5927680DDE00A2DB75 /* RequestStore.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -2205,6 +2264,7 @@
|
||||||
9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */,
|
9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */,
|
||||||
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
|
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
|
||||||
9E9ECC9A28589E150099D5A2 /* RecoveryPhraseValidationFlowSnapshotTests.swift in Sources */,
|
9E9ECC9A28589E150099D5A2 /* RecoveryPhraseValidationFlowSnapshotTests.swift in Sources */,
|
||||||
|
9E0F574B2980260D005304FA /* LoggerTests.swift in Sources */,
|
||||||
9EAFEB822805793200199FC9 /* RootTests.swift in Sources */,
|
9EAFEB822805793200199FC9 /* RootTests.swift in Sources */,
|
||||||
9E391132284644580073DD9A /* AppInitializationTests.swift in Sources */,
|
9E391132284644580073DD9A /* AppInitializationTests.swift in Sources */,
|
||||||
9E9ECC9928589E150099D5A2 /* RecoveryPhraseDisplaySnapshotTests.swift in Sources */,
|
9E9ECC9928589E150099D5A2 /* RecoveryPhraseDisplaySnapshotTests.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
//
|
||||||
|
// LogsHandlerInterface.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 30.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
extension DependencyValues {
|
||||||
|
var logsHandler: LogsHandlerClient {
|
||||||
|
get { self[LogsHandlerClient.self] }
|
||||||
|
set { self[LogsHandlerClient.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct LogsHandlerClient {
|
||||||
|
let exportAndStoreLogs: (URL, URL, URL) async throws -> Void
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// LogsHandlerLive.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 30.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
extension LogsHandlerClient: DependencyKey {
|
||||||
|
static let liveValue = LogsHandlerClient(
|
||||||
|
exportAndStoreLogs: { tempSDKDir, tempTCADir, tempWalletDir in
|
||||||
|
async let sdkLogs = LogsHandlerClient.exportAndStoreLogsFor(key: LoggerConstants.sdkLogs, atURL: tempSDKDir)
|
||||||
|
async let tcaLogs = LogsHandlerClient.exportAndStoreLogsFor(key: LoggerConstants.tcaLogs, atURL: tempTCADir)
|
||||||
|
async let walletLogs = LogsHandlerClient.exportAndStoreLogsFor(key: LoggerConstants.walletLogs, atURL: tempWalletDir)
|
||||||
|
|
||||||
|
let logs = try await [sdkLogs, tcaLogs, walletLogs]
|
||||||
|
|
||||||
|
try logs.forEach { logsHandler in
|
||||||
|
try logsHandler.result.write(to: logsHandler.dir, atomically: true, encoding: String.Encoding.utf8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension LogsHandlerClient {
|
||||||
|
static func exportAndStoreLogsFor(key: String, atURL: URL) async throws -> (result: String, dir: URL) {
|
||||||
|
let logsStr = try await LogStore.exportCategory(key)
|
||||||
|
|
||||||
|
var result = ""
|
||||||
|
logsStr?.forEach({ line in
|
||||||
|
result.append(line)
|
||||||
|
result.append("\n\n")
|
||||||
|
})
|
||||||
|
|
||||||
|
return (result: result, dir: atURL)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
//
|
||||||
|
// LogsHandlerTest.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 30.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ComposableArchitecture
|
||||||
|
import XCTestDynamicOverlay
|
||||||
|
|
||||||
|
extension LogsHandlerClient: TestDependencyKey {
|
||||||
|
static let testValue = Self(
|
||||||
|
exportAndStoreLogs: XCTUnimplemented("\(Self.self).exportAndStoreLogs")
|
||||||
|
)
|
||||||
|
}
|
|
@ -25,6 +25,8 @@ extension RootReducer {
|
||||||
Reduce { state, action in
|
Reduce { state, action in
|
||||||
switch action {
|
switch action {
|
||||||
case .initialization(.appDelegate(.didFinishLaunching)):
|
case .initialization(.appDelegate(.didFinishLaunching)):
|
||||||
|
// TODO: [#524] finish all the wallet events according to definition, https://github.com/zcash/secant-ios-wallet/issues/524
|
||||||
|
LoggerProxy.event(".appDelegate(.didFinishLaunching)")
|
||||||
/// We need to fetch data from keychain, in order to be 100% sure the kecyhain can be read we delay the check a bit
|
/// We need to fetch data from keychain, in order to be 100% sure the kecyhain can be read we delay the check a bit
|
||||||
return Effect(value: .initialization(.checkWalletInitialization))
|
return Effect(value: .initialization(.checkWalletInitialization))
|
||||||
.delay(for: 0.02, scheduler: mainQueue)
|
.delay(for: 0.02, scheduler: mainQueue)
|
||||||
|
|
|
@ -139,7 +139,8 @@ extension RootReducer {
|
||||||
spendParamsURL: try databaseFiles.spendParamsURLFor(network),
|
spendParamsURL: try databaseFiles.spendParamsURLFor(network),
|
||||||
outputParamsURL: try databaseFiles.outputParamsURLFor(network),
|
outputParamsURL: try databaseFiles.outputParamsURLFor(network),
|
||||||
viewingKeys: [viewingKey],
|
viewingKeys: [viewingKey],
|
||||||
walletBirthday: birthday
|
walletBirthday: birthday,
|
||||||
|
loggerProxy: OSLogger_(logLevel: .debug, category: LoggerConstants.sdkLogs)
|
||||||
)
|
)
|
||||||
|
|
||||||
return initializer
|
return initializer
|
||||||
|
@ -173,7 +174,7 @@ extension RootStore {
|
||||||
static var placeholder: RootStore {
|
static var placeholder: RootStore {
|
||||||
RootStore(
|
RootStore(
|
||||||
initialState: .placeholder,
|
initialState: .placeholder,
|
||||||
reducer: RootReducer()._printChanges()
|
reducer: RootReducer().logging()._printChanges()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,39 @@ struct SettingsReducer: ReducerProtocol {
|
||||||
case backupPhrase
|
case backupPhrase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var destination: Destination?
|
||||||
|
var exportLogsDisabled = false
|
||||||
|
var isSharingLogs = false
|
||||||
var phraseDisplayState: RecoveryPhraseDisplayReducer.State
|
var phraseDisplayState: RecoveryPhraseDisplayReducer.State
|
||||||
var rescanDialog: ConfirmationDialogState<SettingsReducer.Action>?
|
var rescanDialog: ConfirmationDialogState<SettingsReducer.Action>?
|
||||||
var destination: Destination?
|
|
||||||
|
var tempSDKDir: URL {
|
||||||
|
let tempDir = FileManager.default.temporaryDirectory
|
||||||
|
let sdkFileName = "sdkLogs.txt"
|
||||||
|
return tempDir.appendingPathComponent(sdkFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempTCADir: URL {
|
||||||
|
let tempDir = FileManager.default.temporaryDirectory
|
||||||
|
let sdkFileName = "tcaLogs.txt"
|
||||||
|
return tempDir.appendingPathComponent(sdkFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempWalletDir: URL {
|
||||||
|
let tempDir = FileManager.default.temporaryDirectory
|
||||||
|
let sdkFileName = "walletLogs.txt"
|
||||||
|
return tempDir.appendingPathComponent(sdkFileName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Action: Equatable {
|
enum Action: Equatable {
|
||||||
case backupWallet
|
case backupWallet
|
||||||
case backupWalletAccessRequest
|
case backupWalletAccessRequest
|
||||||
case cancelRescan
|
case cancelRescan
|
||||||
|
case exportLogs
|
||||||
case fullRescan
|
case fullRescan
|
||||||
|
case logsExported
|
||||||
|
case logsShareFinished
|
||||||
case phraseDisplay(RecoveryPhraseDisplayReducer.Action)
|
case phraseDisplay(RecoveryPhraseDisplayReducer.Action)
|
||||||
case quickRescan
|
case quickRescan
|
||||||
case rescanBlockchain
|
case rescanBlockchain
|
||||||
|
@ -29,6 +52,7 @@ struct SettingsReducer: ReducerProtocol {
|
||||||
@Dependency(\.localAuthentication) var localAuthentication
|
@Dependency(\.localAuthentication) var localAuthentication
|
||||||
@Dependency(\.mnemonic) var mnemonic
|
@Dependency(\.mnemonic) var mnemonic
|
||||||
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
|
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
|
||||||
|
@Dependency(\.logsHandler) var logsHandler
|
||||||
@Dependency(\.walletStorage) var walletStorage
|
@Dependency(\.walletStorage) var walletStorage
|
||||||
|
|
||||||
var body: some ReducerProtocol<State, Action> {
|
var body: some ReducerProtocol<State, Action> {
|
||||||
|
@ -57,6 +81,26 @@ struct SettingsReducer: ReducerProtocol {
|
||||||
state.rescanDialog = nil
|
state.rescanDialog = nil
|
||||||
return .none
|
return .none
|
||||||
|
|
||||||
|
case .exportLogs:
|
||||||
|
state.exportLogsDisabled = true
|
||||||
|
return .run { [state] send in
|
||||||
|
do {
|
||||||
|
try await logsHandler.exportAndStoreLogs(state.tempSDKDir, state.tempTCADir, state.tempWalletDir)
|
||||||
|
await send(.logsExported)
|
||||||
|
} catch {
|
||||||
|
// TODO: [#527] address the error here https://github.com/zcash/secant-ios-wallet/issues/527
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .logsExported:
|
||||||
|
state.exportLogsDisabled = false
|
||||||
|
state.isSharingLogs = true
|
||||||
|
return .none
|
||||||
|
|
||||||
|
case .logsShareFinished:
|
||||||
|
state.isSharingLogs = false
|
||||||
|
return .none
|
||||||
|
|
||||||
case .rescanBlockchain:
|
case .rescanBlockchain:
|
||||||
state.rescanDialog = .init(
|
state.rescanDialog = .init(
|
||||||
title: TextState("Rescan"),
|
title: TextState("Rescan"),
|
||||||
|
|
|
@ -23,6 +23,25 @@ struct SettingsView: View {
|
||||||
.frame(height: 50)
|
.frame(height: 50)
|
||||||
.padding(.horizontal, 30)
|
.padding(.horizontal, 30)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
action: { viewStore.send(.exportLogs) },
|
||||||
|
label: {
|
||||||
|
if viewStore.exportLogsDisabled {
|
||||||
|
HStack {
|
||||||
|
ProgressView()
|
||||||
|
Text("Exporting...")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text("Export & share logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.primaryButtonStyle
|
||||||
|
.frame(height: 50)
|
||||||
|
.padding(.horizontal, 30)
|
||||||
|
.padding(.top, 30)
|
||||||
|
.disabled(viewStore.exportLogsDisabled)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.navigationTitle("Settings")
|
.navigationTitle("Settings")
|
||||||
|
@ -37,6 +56,17 @@ struct SettingsView: View {
|
||||||
RecoveryPhraseDisplayView(store: store.backupPhraseStore())
|
RecoveryPhraseDisplayView(store: store.backupPhraseStore())
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if viewStore.isSharingLogs {
|
||||||
|
UIShareDialogView(
|
||||||
|
activityItems: [viewStore.tempSDKDir, viewStore.tempWalletDir, viewStore.tempTCADir]
|
||||||
|
) {
|
||||||
|
viewStore.send(.logsShareFinished)
|
||||||
|
}
|
||||||
|
// UIShareDialogView only wraps UIActivityViewController presentation
|
||||||
|
// so frame is set to 0 to not break SwiftUIs layout
|
||||||
|
.frame(width: 0, height: 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// UIShareDialog.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 30.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public class UIShareDialog: UIView {
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIShareDialog {
|
||||||
|
public func doInitialSetup(activityItems: [Any], completion: @escaping () -> Void) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
|
||||||
|
|
||||||
|
UIApplication.shared.connectedScenes.map({ $0 as? UIWindowScene })
|
||||||
|
.compactMap({ $0 })
|
||||||
|
.first?.windows.first?.rootViewController?.present(
|
||||||
|
activityVC,
|
||||||
|
animated: true,
|
||||||
|
completion: completion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UIShareDialogView: UIViewRepresentable {
|
||||||
|
let activityItems: [Any]
|
||||||
|
let completion: () -> Void
|
||||||
|
|
||||||
|
public func makeUIView(context: UIViewRepresentableContext<UIShareDialogView>) -> UIShareDialog {
|
||||||
|
let view = UIShareDialog()
|
||||||
|
view.doInitialSetup(activityItems: activityItems, completion: completion)
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateUIView(_ uiView: UIShareDialog, context: UIViewRepresentableContext<UIShareDialogView>) {
|
||||||
|
// We can leave it empty here because the view is just handler how to bridge UIKit's UIActivityViewController
|
||||||
|
// presentation into SwiftUI. The view itself is not visible, only instantiated, therefore no updates needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
public typealias UIViewType = UIShareDialog
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
|
walletLogger = OSLogger_(logLevel: .debug, category: LoggerConstants.walletLogs)
|
||||||
// set the default behavior for the NSDecimalNumber
|
// set the default behavior for the NSDecimalNumber
|
||||||
NSDecimalNumber.defaultBehavior = Zatoshi.decimalHandler
|
NSDecimalNumber.defaultBehavior = Zatoshi.decimalHandler
|
||||||
rootViewStore.send(.initialization(.appDelegate(.didFinishLaunching)))
|
rootViewStore.send(.initialization(.appDelegate(.didFinishLaunching)))
|
||||||
|
|
|
@ -8,12 +8,25 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Date {
|
extension Date {
|
||||||
|
static let timestampFormatter: DateFormatter = {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||||
|
formatter.dateFormat = "yyyy/MM/dd HH:mm:ss.SSSS"
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
static let humanReadableFormatter: DateFormatter = {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateStyle = .short
|
||||||
|
formatter.timeStyle = .short
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
func timestamp() -> String {
|
||||||
|
return String(format: "%@", Date.timestampFormatter.string(from: self))
|
||||||
|
}
|
||||||
|
|
||||||
func asHumanReadable() -> String {
|
func asHumanReadable() -> String {
|
||||||
let dateFormatter = DateFormatter()
|
return Date.humanReadableFormatter.string(from: self)
|
||||||
|
|
||||||
dateFormatter.dateStyle = .short
|
|
||||||
dateFormatter.timeStyle = .short
|
|
||||||
|
|
||||||
return dateFormatter.string(from: self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// LogStore.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 23.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import OSLog
|
||||||
|
|
||||||
|
enum LogStore {
|
||||||
|
static func exportCategory(
|
||||||
|
_ category: String,
|
||||||
|
hoursToThePast: TimeInterval = 168,
|
||||||
|
fileSize: Int = 1_000_000
|
||||||
|
) async throws -> [String]? {
|
||||||
|
guard let bundle = Bundle.main.bundleIdentifier else { return nil }
|
||||||
|
|
||||||
|
let store = try OSLogStore(scope: .currentProcessIdentifier)
|
||||||
|
let date = Date.now.addingTimeInterval(-hoursToThePast * 3600)
|
||||||
|
let position = store.position(date: date)
|
||||||
|
var res: [String] = []
|
||||||
|
var size = 0
|
||||||
|
|
||||||
|
let entries = try store.getEntries(at: position).reversed()
|
||||||
|
for entry in entries {
|
||||||
|
guard let logEntry = entry as? OSLogEntryLog else { continue }
|
||||||
|
guard logEntry.subsystem == bundle && logEntry.category == category else { continue }
|
||||||
|
|
||||||
|
guard size < fileSize else { break }
|
||||||
|
|
||||||
|
size += logEntry.composedMessage.utf8.count
|
||||||
|
res.append("[\(logEntry.date.timestamp())] \(logEntry.composedMessage)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
//
|
||||||
|
// OSLogger_.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 23.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ZcashLightClientKit
|
||||||
|
import os
|
||||||
|
|
||||||
|
enum LoggerConstants {
|
||||||
|
static let sdkLogs = "sdkLogs"
|
||||||
|
static let tcaLogs = "tcaLogs"
|
||||||
|
static let walletLogs = "walletLogs"
|
||||||
|
}
|
||||||
|
|
||||||
|
var walletLogger: ZcashLightClientKit.Logger?
|
||||||
|
|
||||||
|
enum LoggerProxy {
|
||||||
|
static func debug(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||||
|
walletLogger?.debug(message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func info(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||||
|
walletLogger?.info(message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func event(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||||
|
walletLogger?.event(message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func warn(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||||
|
walletLogger?.warn(message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func error(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||||
|
walletLogger?.error(message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: [#529] the swiftlint rule as well as OSLogger_ will be removed once secant adopts latest SDK changes https://github.com/zcash/secant-ios-wallet/issues/529
|
||||||
|
// swiftlint:disable:next type_name
|
||||||
|
class OSLogger_: ZcashLightClientKit.Logger {
|
||||||
|
enum LogLevel: Int {
|
||||||
|
case debug
|
||||||
|
case error
|
||||||
|
case warning
|
||||||
|
case event
|
||||||
|
case info
|
||||||
|
}
|
||||||
|
|
||||||
|
private(set) var oslog: OSLog?
|
||||||
|
|
||||||
|
var level: LogLevel
|
||||||
|
|
||||||
|
init(logLevel: LogLevel, category: String) {
|
||||||
|
self.level = logLevel
|
||||||
|
if let bundleName = Bundle.main.bundleIdentifier {
|
||||||
|
self.oslog = OSLog(subsystem: bundleName, category: category)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func debug(
|
||||||
|
_ message: String,
|
||||||
|
file: StaticString = #file,
|
||||||
|
function: StaticString = #function,
|
||||||
|
line: Int = #line
|
||||||
|
) {
|
||||||
|
guard level.rawValue == LogLevel.debug.rawValue else { return }
|
||||||
|
log(level: "DEBUG 🐞", message: message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func error(
|
||||||
|
_ message: String,
|
||||||
|
file: StaticString = #file,
|
||||||
|
function: StaticString = #function,
|
||||||
|
line: Int = #line
|
||||||
|
) {
|
||||||
|
guard level.rawValue <= LogLevel.error.rawValue else { return }
|
||||||
|
log(level: "ERROR 💥", message: message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func warn(
|
||||||
|
_ message: String,
|
||||||
|
file: StaticString = #file,
|
||||||
|
function: StaticString = #function,
|
||||||
|
line: Int = #line
|
||||||
|
) {
|
||||||
|
guard level.rawValue <= LogLevel.warning.rawValue else { return }
|
||||||
|
log(level: "WARNING ⚠️", message: message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func event(
|
||||||
|
_ message: String,
|
||||||
|
file: StaticString = #file,
|
||||||
|
function: StaticString = #function,
|
||||||
|
line: Int = #line
|
||||||
|
) {
|
||||||
|
guard level.rawValue <= LogLevel.event.rawValue else { return }
|
||||||
|
log(level: "EVENT ⏱", message: message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func info(
|
||||||
|
_ message: String,
|
||||||
|
file: StaticString = #file,
|
||||||
|
function: StaticString = #function,
|
||||||
|
line: Int = #line
|
||||||
|
) {
|
||||||
|
guard level.rawValue <= LogLevel.info.rawValue else { return }
|
||||||
|
log(level: "INFO ℹ️", message: message, file: file, function: function, line: line)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func log(
|
||||||
|
level: String,
|
||||||
|
message: String,
|
||||||
|
file: StaticString = #file,
|
||||||
|
function: StaticString = #function,
|
||||||
|
line: Int = #line
|
||||||
|
) {
|
||||||
|
guard let oslog else { return }
|
||||||
|
|
||||||
|
let fileName = (String(describing: file) as NSString).lastPathComponent
|
||||||
|
|
||||||
|
os_log(
|
||||||
|
"[%{public}@] %{public}@ - %{public}@ - Line: %{public}d -> %{public}@",
|
||||||
|
log: oslog,
|
||||||
|
level,
|
||||||
|
fileName,
|
||||||
|
String(describing: function),
|
||||||
|
line,
|
||||||
|
message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// TCALogger.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 23.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import os
|
||||||
|
|
||||||
|
class TCALogger: OSLogger_ { }
|
||||||
|
|
||||||
|
extension TCALogger {
|
||||||
|
static let live = TCALogger(logLevel: .debug, category: LoggerConstants.tcaLogs)
|
||||||
|
|
||||||
|
func tcaDebug(_ message: String) {
|
||||||
|
guard let oslog else { return }
|
||||||
|
|
||||||
|
os_log(
|
||||||
|
"%{public}@",
|
||||||
|
log: oslog,
|
||||||
|
message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
//
|
||||||
|
// TCALoggerReducer.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 23.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
extension ReducerProtocol {
|
||||||
|
@inlinable
|
||||||
|
public func logging(
|
||||||
|
_ logger: ReducerLogger<State, Action>? = .tcaLogger
|
||||||
|
) -> LogChangesReducer<Self> {
|
||||||
|
LogChangesReducer<Self>(base: self, logger: logger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ReducerLogger<State, Action> {
|
||||||
|
private let _logChange: (_ receivedAction: Action, _ oldState: State, _ newState: State) -> Void
|
||||||
|
|
||||||
|
public init(
|
||||||
|
logChange: @escaping (_ receivedAction: Action, _ oldState: State, _ newState: State) -> Void
|
||||||
|
) {
|
||||||
|
self._logChange = logChange
|
||||||
|
}
|
||||||
|
|
||||||
|
public func logChange(receivedAction: Action, oldState: State, newState: State) {
|
||||||
|
self._logChange(receivedAction, oldState, newState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ReducerLogger {
|
||||||
|
public static var tcaLogger: Self {
|
||||||
|
Self { receivedAction, oldState, newState in
|
||||||
|
var target = ""
|
||||||
|
target.write("received action:\n")
|
||||||
|
CustomDump.customDump(receivedAction, to: &target, indent: 2)
|
||||||
|
target.write("\n")
|
||||||
|
target.write(diff(oldState, newState).map { "\($0)\n" } ?? " (No state changes)\n")
|
||||||
|
TCALogger.live.tcaDebug("\(target)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct LogChangesReducer<Base: ReducerProtocol>: ReducerProtocol {
|
||||||
|
@usableFromInline let base: Base
|
||||||
|
|
||||||
|
@usableFromInline let logger: ReducerLogger<Base.State, Base.Action>?
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
init(base: Base, logger: ReducerLogger<Base.State, Base.Action>?) {
|
||||||
|
self.base = base
|
||||||
|
self.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
public func reduce(
|
||||||
|
into state: inout Base.State, action: Base.Action
|
||||||
|
) -> EffectTask<Base.Action> {
|
||||||
|
guard let logger else {
|
||||||
|
return self.base.reduce(into: &state, action: action)
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldState = state
|
||||||
|
let effects = self.base.reduce(into: &state, action: action)
|
||||||
|
return effects.merge(
|
||||||
|
with: .fireAndForget { [newState = state] in
|
||||||
|
logger.logChange(receivedAction: action, oldState: oldState, newState: newState)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -99,6 +99,7 @@ class SettingsTests: XCTestCase {
|
||||||
func testRescanBlockchain_Cancelling() async throws {
|
func testRescanBlockchain_Cancelling() async throws {
|
||||||
let store = TestStore(
|
let store = TestStore(
|
||||||
initialState: SettingsReducer.State(
|
initialState: SettingsReducer.State(
|
||||||
|
destination: nil,
|
||||||
phraseDisplayState: .init(),
|
phraseDisplayState: .init(),
|
||||||
rescanDialog: .init(
|
rescanDialog: .init(
|
||||||
title: TextState("Rescan"),
|
title: TextState("Rescan"),
|
||||||
|
@ -108,8 +109,7 @@ class SettingsTests: XCTestCase {
|
||||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||||
.cancel(TextState("Cancel"))
|
.cancel(TextState("Cancel"))
|
||||||
]
|
]
|
||||||
),
|
)
|
||||||
destination: nil
|
|
||||||
),
|
),
|
||||||
reducer: SettingsReducer()
|
reducer: SettingsReducer()
|
||||||
)
|
)
|
||||||
|
@ -122,6 +122,7 @@ class SettingsTests: XCTestCase {
|
||||||
func testRescanBlockchain_QuickRescanClearance() async throws {
|
func testRescanBlockchain_QuickRescanClearance() async throws {
|
||||||
let store = TestStore(
|
let store = TestStore(
|
||||||
initialState: SettingsReducer.State(
|
initialState: SettingsReducer.State(
|
||||||
|
destination: nil,
|
||||||
phraseDisplayState: .init(),
|
phraseDisplayState: .init(),
|
||||||
rescanDialog: .init(
|
rescanDialog: .init(
|
||||||
title: TextState("Rescan"),
|
title: TextState("Rescan"),
|
||||||
|
@ -131,8 +132,7 @@ class SettingsTests: XCTestCase {
|
||||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||||
.cancel(TextState("Cancel"))
|
.cancel(TextState("Cancel"))
|
||||||
]
|
]
|
||||||
),
|
)
|
||||||
destination: nil
|
|
||||||
),
|
),
|
||||||
reducer: SettingsReducer()
|
reducer: SettingsReducer()
|
||||||
)
|
)
|
||||||
|
@ -145,6 +145,7 @@ class SettingsTests: XCTestCase {
|
||||||
func testRescanBlockchain_FullRescanClearance() async throws {
|
func testRescanBlockchain_FullRescanClearance() async throws {
|
||||||
let store = TestStore(
|
let store = TestStore(
|
||||||
initialState: SettingsReducer.State(
|
initialState: SettingsReducer.State(
|
||||||
|
destination: nil,
|
||||||
phraseDisplayState: .init(),
|
phraseDisplayState: .init(),
|
||||||
rescanDialog: .init(
|
rescanDialog: .init(
|
||||||
title: TextState("Rescan"),
|
title: TextState("Rescan"),
|
||||||
|
@ -154,8 +155,7 @@ class SettingsTests: XCTestCase {
|
||||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||||
.cancel(TextState("Cancel"))
|
.cancel(TextState("Cancel"))
|
||||||
]
|
]
|
||||||
),
|
)
|
||||||
destination: nil
|
|
||||||
),
|
),
|
||||||
reducer: SettingsReducer()
|
reducer: SettingsReducer()
|
||||||
)
|
)
|
||||||
|
@ -164,4 +164,58 @@ class SettingsTests: XCTestCase {
|
||||||
state.rescanDialog = nil
|
state.rescanDialog = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testExportLogs_ButtonDisableShareEnable() async throws {
|
||||||
|
let store = TestStore(
|
||||||
|
initialState: SettingsReducer.State(
|
||||||
|
destination: nil,
|
||||||
|
phraseDisplayState: .init(),
|
||||||
|
rescanDialog: .init(
|
||||||
|
title: TextState("Rescan"),
|
||||||
|
message: TextState("Select the rescan you want"),
|
||||||
|
buttons: [
|
||||||
|
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||||
|
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||||
|
.cancel(TextState("Cancel"))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
reducer: SettingsReducer()
|
||||||
|
)
|
||||||
|
|
||||||
|
store.dependencies.logsHandler = LogsHandlerClient(exportAndStoreLogs: { _, _, _ in })
|
||||||
|
|
||||||
|
_ = await store.send(.exportLogs) { state in
|
||||||
|
state.exportLogsDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
await store.receive(.logsExported) { state in
|
||||||
|
state.exportLogsDisabled = false
|
||||||
|
state.isSharingLogs = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLogShareFinished() async throws {
|
||||||
|
let store = TestStore(
|
||||||
|
initialState: SettingsReducer.State(
|
||||||
|
destination: nil,
|
||||||
|
isSharingLogs: true,
|
||||||
|
phraseDisplayState: .init(),
|
||||||
|
rescanDialog: .init(
|
||||||
|
title: TextState("Rescan"),
|
||||||
|
message: TextState("Select the rescan you want"),
|
||||||
|
buttons: [
|
||||||
|
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||||
|
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||||
|
.cancel(TextState("Cancel"))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
reducer: SettingsReducer()
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = await store.send(.logsShareFinished) { state in
|
||||||
|
state.isSharingLogs = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
//
|
||||||
|
// LoggerTests.swift
|
||||||
|
// secantTests
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 24.01.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import secant_testnet
|
||||||
|
import OSLog
|
||||||
|
|
||||||
|
class LoggerTests: XCTestCase {
|
||||||
|
func testOSLogger_loggingAndExport() throws {
|
||||||
|
let category = "testOSLogger_loggingAndExport"
|
||||||
|
let osLogger = OSLogger_(logLevel: .debug, category: category)
|
||||||
|
let testMessage = "test message"
|
||||||
|
|
||||||
|
osLogger.debug(testMessage)
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
XCTAssertNotNil(logs)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 1)
|
||||||
|
|
||||||
|
let loggedMessage = logs[0].osLoggedMessage()
|
||||||
|
|
||||||
|
XCTAssertEqual(testMessage, loggedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_DebugLevel_DebugLog() throws {
|
||||||
|
let category = "testOSLogger_DebugLevel_DebugLog"
|
||||||
|
let osLogger = OSLogger_(logLevel: .debug, category: category)
|
||||||
|
let testMessage = "debug message"
|
||||||
|
|
||||||
|
osLogger.debug(testMessage)
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
XCTAssertNotNil(logs)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 1)
|
||||||
|
|
||||||
|
let loggedMessage = logs[0].osLoggedMessage()
|
||||||
|
|
||||||
|
XCTAssertEqual(testMessage, loggedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_ErrorLevel_ErrorLog() throws {
|
||||||
|
let category = "testOSLogger_ErrorLevel_ErrorLog"
|
||||||
|
let osLogger = OSLogger_(logLevel: .debug, category: category)
|
||||||
|
let testMessage = "error message"
|
||||||
|
|
||||||
|
osLogger.error(testMessage)
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
XCTAssertNotNil(logs)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 1)
|
||||||
|
|
||||||
|
let loggedMessage = logs[0].osLoggedMessage()
|
||||||
|
|
||||||
|
XCTAssertEqual(testMessage, loggedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_WarningLevel_WarningLog() throws {
|
||||||
|
let category = "testOSLogger_WarningLevel_WarningLog"
|
||||||
|
let osLogger = OSLogger_(logLevel: .warning, category: category)
|
||||||
|
let testMessage = "warning message"
|
||||||
|
|
||||||
|
osLogger.warn(testMessage)
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
XCTAssertNotNil(logs)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 1)
|
||||||
|
|
||||||
|
let loggedMessage = logs[0].osLoggedMessage()
|
||||||
|
|
||||||
|
XCTAssertEqual(testMessage, loggedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_EventLevel_EventLog() throws {
|
||||||
|
let category = "testOSLogger_EventLevel_EventLog"
|
||||||
|
let osLogger = OSLogger_(logLevel: .event, category: category)
|
||||||
|
let testMessage = "event message"
|
||||||
|
|
||||||
|
osLogger.event(testMessage)
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
XCTAssertNotNil(logs)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 1)
|
||||||
|
|
||||||
|
let loggedMessage = logs[0].osLoggedMessage()
|
||||||
|
|
||||||
|
XCTAssertEqual(testMessage, loggedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_InfoLevel_InfoLog() throws {
|
||||||
|
let category = "testOSLogger_InfoLevel_InfoLog"
|
||||||
|
let osLogger = OSLogger_(logLevel: .info, category: category)
|
||||||
|
let testMessage = "info message"
|
||||||
|
|
||||||
|
osLogger.info(testMessage)
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
XCTAssertNotNil(logs)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 1)
|
||||||
|
|
||||||
|
let loggedMessage = logs[0].osLoggedMessage()
|
||||||
|
|
||||||
|
XCTAssertEqual(testMessage, loggedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_DebugLevel_OtherLogs() throws {
|
||||||
|
let category = "testOSLogger_DebugLevel_OtherLogs"
|
||||||
|
let osLogger = OSLogger_(logLevel: .debug, category: category)
|
||||||
|
let testMessage = "debug message"
|
||||||
|
|
||||||
|
osLogger.debug(testMessage)
|
||||||
|
osLogger.error(testMessage)
|
||||||
|
osLogger.warn(testMessage)
|
||||||
|
osLogger.event(testMessage)
|
||||||
|
osLogger.info(testMessage)
|
||||||
|
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_ErrorLevel_OtherLogs() throws {
|
||||||
|
let category = "testOSLogger_ErrorLevel_OtherLogs"
|
||||||
|
let osLogger = OSLogger_(logLevel: .error, category: category)
|
||||||
|
let testMessage = "debug message"
|
||||||
|
|
||||||
|
osLogger.debug(testMessage)
|
||||||
|
osLogger.error(testMessage)
|
||||||
|
osLogger.warn(testMessage)
|
||||||
|
osLogger.event(testMessage)
|
||||||
|
osLogger.info(testMessage)
|
||||||
|
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_WarningLevel_OtherLogs() throws {
|
||||||
|
let category = "testOSLogger_WarningLevel_OtherLogs"
|
||||||
|
let osLogger = OSLogger_(logLevel: .warning, category: category)
|
||||||
|
let testMessage = "debug message"
|
||||||
|
|
||||||
|
osLogger.debug(testMessage)
|
||||||
|
osLogger.error(testMessage)
|
||||||
|
osLogger.warn(testMessage)
|
||||||
|
osLogger.event(testMessage)
|
||||||
|
osLogger.info(testMessage)
|
||||||
|
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_EventLevel_OtherLogs() throws {
|
||||||
|
let category = "testOSLogger_EventLevel_OtherLogs"
|
||||||
|
let osLogger = OSLogger_(logLevel: .event, category: category)
|
||||||
|
let testMessage = "debug message"
|
||||||
|
|
||||||
|
osLogger.debug(testMessage)
|
||||||
|
osLogger.error(testMessage)
|
||||||
|
osLogger.warn(testMessage)
|
||||||
|
osLogger.event(testMessage)
|
||||||
|
osLogger.info(testMessage)
|
||||||
|
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLogger_InfoLevel_OtherLogs() throws {
|
||||||
|
let category = "testOSLogger_InfoLevel_OtherLogs"
|
||||||
|
let osLogger = OSLogger_(logLevel: .info, category: category)
|
||||||
|
let testMessage = "debug message"
|
||||||
|
|
||||||
|
osLogger.debug(testMessage)
|
||||||
|
osLogger.error(testMessage)
|
||||||
|
osLogger.warn(testMessage)
|
||||||
|
osLogger.event(testMessage)
|
||||||
|
osLogger.info(testMessage)
|
||||||
|
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testWalletLogger() throws {
|
||||||
|
let category = "testWalletLogger"
|
||||||
|
walletLogger = OSLogger_(logLevel: .info, category: category)
|
||||||
|
let testMessage = "wallet test message"
|
||||||
|
|
||||||
|
LoggerProxy.info(testMessage)
|
||||||
|
let logs = TestLogStore.exportCategory(category)
|
||||||
|
|
||||||
|
XCTAssertNotNil(logs)
|
||||||
|
|
||||||
|
guard let logs else { return }
|
||||||
|
|
||||||
|
XCTAssertEqual(logs.count, 1)
|
||||||
|
|
||||||
|
let loggedMessage = logs[0].osLoggedMessage()
|
||||||
|
|
||||||
|
XCTAssertEqual(testMessage, loggedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
func osLoggedMessage() -> String? {
|
||||||
|
let split = components(separatedBy: "-> ")
|
||||||
|
|
||||||
|
if split.count == 2 {
|
||||||
|
return split[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TestLogStore {
|
||||||
|
static func exportCategory(_ category: String, hoursToThePast: TimeInterval = 24) -> [String]? {
|
||||||
|
guard let bundle = Bundle.main.bundleIdentifier else { return nil }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let store = try OSLogStore(scope: .currentProcessIdentifier)
|
||||||
|
let date = Date.now.addingTimeInterval(-hoursToThePast * 3600)
|
||||||
|
let position = store.position(date: date)
|
||||||
|
var entries: [String] = []
|
||||||
|
|
||||||
|
entries = try store
|
||||||
|
.getEntries(at: position)
|
||||||
|
.compactMap { $0 as? OSLogEntryLog }
|
||||||
|
.filter { $0.subsystem == bundle && $0.category == category }
|
||||||
|
.map { "[\($0.date.formatted())] \($0.composedMessage)" }
|
||||||
|
|
||||||
|
return entries
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue