[#302] Synchronizer status on Home Screen (#304)

* synchronizer status

* amount input field enhancements

Closes #302
This commit is contained in:
Lukas Korba 2022-05-04 22:01:48 +02:00 committed by GitHub
parent f6e6f6991f
commit 6a12e09ee9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 365 additions and 94 deletions

View File

@ -106,6 +106,7 @@
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 */; };
9E69A24D27FB002800A55317 /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E69A24C27FB002800A55317 /* Welcome.swift */; };
9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */; };
9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppReducerTests.swift */; };
@ -274,6 +275,7 @@
9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionFailedView.swift; sourceTree = "<group>"; };
9E5BF643281FEC9900BA3F17 /* SendTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendTests.swift; sourceTree = "<group>"; };
9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedUserDefaults.swift; sourceTree = "<group>"; };
9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedNotificationCenter.swift; sourceTree = "<group>"; };
9E69A24C27FB002800A55317 /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = "<group>"; };
9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = "<group>"; };
9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = "<group>"; };
@ -762,6 +764,7 @@
9E02B5C2280458D2005B809B /* WrappedDerivationTool.swift */,
9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */,
9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */,
9E5BF647282277BE00BA3F17 /* WrappedNotificationCenter.swift */,
);
path = Wrappers;
sourceTree = "<group>";
@ -1232,6 +1235,7 @@
0DC487C32772574C00BE6A63 /* ValidationSucceededView.swift in Sources */,
2EB1C5E827D77F6100BC43D7 /* TextFieldStore.swift in Sources */,
9EAFEB8A2806F48100199FC9 /* ZCashSDKEnvironment.swift in Sources */,
9E5BF648282277BE00BA3F17 /* WrappedNotificationCenter.swift in Sources */,
0D8A43C4272AEEDE005A6414 /* SecantTextStyles.swift in Sources */,
9E5BF641281FD7B600BA3F17 /* TransactionFailedView.swift in Sources */,
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */,

View File

@ -1,5 +1,6 @@
import ComposableArchitecture
import SwiftUI
import ZcashLightClientKit
struct HomeState: Equatable {
enum Route: Equatable {
@ -16,6 +17,7 @@ struct HomeState: Equatable {
var requestState: RequestState
var sendState: SendState
var scanState: ScanState
var synchronizerStatus: String
var totalBalance: Double
var transactionHistoryState: TransactionHistoryState
var verifiedBalance: Double
@ -34,6 +36,7 @@ enum HomeAction: Equatable {
case updateBalance(Balance)
case updateDrawer(DrawerOverlay)
case updateRoute(HomeState.Route?)
case updateSynchronizerStatus
case updateTransactions([TransactionState])
}
@ -83,20 +86,19 @@ extension HomeReducer {
.receive(on: environment.scheduler)
.map({ Balance(verified: $0.verified, total: $0.total) })
.map(HomeAction.updateBalance)
.eraseToEffect()
.eraseToEffect(),
Effect(value: .updateSynchronizerStatus)
)
case .synchronizerStateChanged(let synchronizerState):
return .none
return Effect(value: .updateSynchronizerStatus)
case .updateBalance(let balance):
state.totalBalance = balance.total.asHumanReadableZecBalance()
state.verifiedBalance = balance.verified.asHumanReadableZecBalance()
return .none
case .debugMenuStartup:
return .none
case .updateDrawer(let drawerOverlay):
state.drawerOverlay = drawerOverlay
state.transactionHistoryState.isScrollable = drawerOverlay == .full ? true : false
@ -105,6 +107,10 @@ extension HomeReducer {
case .updateTransactions(let transactions):
return .none
case .updateSynchronizerStatus:
state.synchronizerStatus = environment.wrappedSDKSynchronizer.status()
return .none
case .updateRoute(let route):
state.route = route
return .none
@ -132,6 +138,9 @@ extension HomeReducer {
case .send(let action):
return .none
case .debugMenuStartup:
return .none
}
}
@ -234,9 +243,46 @@ extension HomeState {
requestState: .placeholder,
sendState: .placeholder,
scanState: .placeholder,
synchronizerStatus: "",
totalBalance: 0.0,
transactionHistoryState: .emptyPlaceHolder,
verifiedBalance: 0.0
)
}
}
extension SDKSynchronizer {
static func textFor(state: SyncStatus) -> String {
switch state {
case .downloading(let progress):
return "Downloading \(progress.progressHeight)/\(progress.targetHeight)"
case .enhancing(let enhanceProgress):
return "Enhancing tx \(enhanceProgress.enhancedTransactions) of \(enhanceProgress.totalTransactions)"
case .fetching:
return "fetching UTXOs"
case .scanning(let scanProgress):
return "Scanning: \(scanProgress.progressHeight)/\(scanProgress.targetHeight)"
case .disconnected:
return "disconnected 💔"
case .stopped:
return "Stopped 🚫"
case .synced:
return "Synced 😎"
case .unprepared:
return "Unprepared 😅"
case .validating:
return "Validating"
case .error(let err):
return "Error: \(err.localizedDescription)"
}
}
}

View File

@ -15,11 +15,14 @@ struct HomeView: View {
sendButton(viewStore)
VStack {
Text("\(viewStore.synchronizerStatus)")
.padding(.top, 60)
Text("balance: \(viewStore.totalBalance)")
.accessDebugMenuWithHiddenGesture {
viewStore.send(.debugMenuStartup)
}
.padding(.top, 180)
.padding(.top, 120)
Spacer()
}

View File

@ -19,7 +19,6 @@ struct SandboxView: View {
case .history:
TransactionHistoryView(store: store.historyStore())
case .send:
EmptyView()
SendView(
store: .init(
initialState: .placeholder,

View File

@ -35,12 +35,19 @@ struct SendState: Equatable {
var route: Route?
var isSendingTransaction = false
var totalBalance = 0.0
var transaction: Transaction
var transactionInputState: TransactionInputState
}
enum SendAction: Equatable {
case onAppear
case onDisappear
case sendConfirmationPressed
case sendTransactionResult(Result<TransactionState, NSError>)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case transactionInput(TransactionInputAction)
case updateBalance(Double)
case updateTransaction(Transaction)
case updateRoute(SendState.Route?)
}
@ -55,12 +62,23 @@ struct SendEnvironment {
// MARK: - SendReducer
private struct ListenerId: Hashable {}
typealias SendReducer = Reducer<SendState, SendAction, SendEnvironment>
extension SendReducer {
private struct SyncStatusUpdatesID: Hashable {}
static let `default` = Reducer<SendState, SendAction, SendEnvironment> { state, action, environment in
static let `default` = SendReducer.combine(
[
balanceReducer,
sendReducer,
transactionInputReducer
]
)
.debug()
private static let sendReducer = SendReducer { state, action, environment in
switch action {
case let .updateTransaction(transaction):
state.transaction = transaction
@ -111,9 +129,51 @@ extension SendReducer {
} catch {
return Effect(value: .updateRoute(.failure))
}
case .transactionInput(let transactionInput):
return .none
default:
return .none
}
}
private static let balanceReducer = SendReducer { state, action, environment in
switch action {
case .onAppear:
return environment.wrappedSDKSynchronizer.stateChanged
.map(SendAction.synchronizerStateChanged)
.eraseToEffect()
.cancellable(id: ListenerId(), cancelInFlight: true)
case .onDisappear:
return Effect.cancel(id: ListenerId())
case .synchronizerStateChanged(.synced):
return environment.wrappedSDKSynchronizer.getShieldedBalance()
.receive(on: environment.scheduler)
.map({ Double($0.total) / Double(100_000_000) })
.map(SendAction.updateBalance)
.eraseToEffect()
case .synchronizerStateChanged(let synchronizerState):
return .none
case .updateBalance(let balance):
state.totalBalance = balance
state.transactionInputState.maxValue = Int64(balance * 100_000_000)
return .none
default:
return .none
}
}
private static let transactionInputReducer: SendReducer = TransactionInputReducer.default.pullback(
state: \SendState.transactionInputState,
action: /SendAction.transactionInput,
environment: { _ in TransactionInputEnvironment() }
)
static func `default`(whenDone: @escaping () -> Void) -> SendReducer {
SendReducer { state, action, environment in
switch action {
@ -176,13 +236,24 @@ extension SendViewStore {
embed: { $0 ? SendState.Route.done : SendState.Route.showConfirmation }
)
}
var bindingForBalance: Binding<Double> {
self.binding(
get: \.totalBalance,
send: SendAction.updateBalance
)
}
}
// MARK: PlaceHolders
extension SendState {
static var placeholder: Self {
.init(route: nil, transaction: .placeholder)
.init(
route: nil,
transaction: .placeholder,
transactionInputState: .placeholer
)
}
static var emptyPlaceholder: Self {
@ -192,7 +263,8 @@ extension SendState {
amount: 0,
memo: "",
toAddress: ""
)
),
transactionInputState: .placeholer
)
}
}

View File

@ -2,61 +2,76 @@ import SwiftUI
import ComposableArchitecture
struct CreateTransaction: View {
let store: TransactionInputStore
@Binding var transaction: Transaction
@Binding var isComplete: Bool
@Binding var totalBalance: Double
var body: some View {
UITextView.appearance().backgroundColor = .clear
return VStack {
return WithViewStore(store) { viewStore in
VStack {
Text("ZEC Amount")
TextField(
"ZEC Amount",
text: $transaction.amountString
)
VStack {
Text("Balance \(totalBalance)")
SingleLineTextField(
placeholderText: "0",
title: "How much ZEC would you like to send?",
store: store.scope(
state: \.textFieldState,
action: TransactionInputAction.textField
),
titleAccessoryView: {
Button(
action: { viewStore.send(.setMax(viewStore.maxValue)) },
label: { Text("Max") }
)
.textFieldTitleAccessoryButtonStyle
},
inputAccessoryView: {
}
)
}
.padding()
.background(Color.white)
.foregroundColor(Asset.Colors.Text.importSeedEditor.color)
}
.padding()
VStack {
Text("To Address")
TextField(
"Address",
text: $transaction.toAddress
)
.font(.system(size: 14))
VStack {
Text("To Address")
TextField(
"Address",
text: $transaction.toAddress
)
.font(.system(size: 14))
.padding()
.background(Color.white)
.foregroundColor(Asset.Colors.Text.importSeedEditor.color)
}
.padding()
.background(Color.white)
.foregroundColor(Asset.Colors.Text.importSeedEditor.color)
}
.padding()
VStack {
Text("Memo")
TextEditor(text: $transaction.memo)
.frame(maxWidth: .infinity, maxHeight: 150, alignment: .center)
.importSeedEditorModifier()
VStack {
Text("Memo")
TextEditor(text: $transaction.memo)
.frame(maxWidth: .infinity, maxHeight: 150, alignment: .center)
.importSeedEditorModifier()
}
.padding()
Button(
action: { isComplete = true },
label: { Text("Send") }
)
.activeButtonStyle
.frame(height: 50)
.padding()
Spacer()
}
.padding()
Button(
action: { isComplete = true },
label: { Text("Send") }
)
.activeButtonStyle
.frame(height: 50)
.padding()
Spacer()
.applyScreenBackground()
}
.padding()
.applyScreenBackground()
}
}
@ -68,12 +83,15 @@ struct Create_Previews: PreviewProvider {
StateContainer(
initialState: (
Transaction.placeholder,
false
false,
0.0
)
) {
CreateTransaction(
store: .placeholder,
transaction: $0.0,
isComplete: $0.1
isComplete: $0.1,
totalBalance: $0.2
)
}
.navigationBarTitleDisplayMode(.inline)
@ -88,7 +106,8 @@ extension SendStore {
return SendStore(
initialState: .init(
route: nil,
transaction: .placeholder
transaction: .placeholder,
transactionInputState: .placeholer
),
reducer: .default,
environment: SendEnvironment(

View File

@ -7,9 +7,16 @@ struct SendView: View {
var body: some View {
WithViewStore(store) { viewStore in
CreateTransaction(
store: store.scope(
state: \.transactionInputState,
action: SendAction.transactionInput
),
transaction: viewStore.bindingForTransaction,
isComplete: viewStore.bindingForConfirmation
isComplete: viewStore.bindingForConfirmation,
totalBalance: viewStore.bindingForBalance
)
.onAppear { viewStore.send(.onAppear) }
.onDisappear { viewStore.send(.onDisappear) }
.navigationLinkEmpty(
isActive: viewStore.bindingForConfirmation,
destination: {
@ -35,7 +42,8 @@ struct SendView_Previews: PreviewProvider {
store: .init(
initialState: .init(
route: nil,
transaction: .placeholder
transaction: .placeholder,
transactionInputState: .placeholer
),
reducer: .default,
environment: SendEnvironment(

View File

@ -6,7 +6,7 @@ struct TransactionConfirmation: View {
var body: some View {
VStack {
Text("Send \(String(format: "%.7f", Int64(viewStore.transaction.amount).asHumanReadableZecBalance())) ZEC")
Text("Send \(String(format: "%.7f", Int64(viewStore.transactionInputState.amount).asHumanReadableZecBalance())) ZEC")
.padding()
Text("To \(viewStore.transaction.toAddress)")

View File

@ -21,8 +21,8 @@ struct TextFieldState: Equatable {
}
}
enum TextFieldAction {
case apply((String) -> String)
enum TextFieldAction: Equatable {
// case apply((String) -> String)
case set(String)
}
@ -31,10 +31,9 @@ struct TextFieldEnvironment: Equatable { }
extension TextFieldReducer {
static let `default` = TextFieldReducer { state, action, _ in
switch action {
case .apply(let action):
state.text = action(state.text)
state.valid = state.text.isValid(for: state.validationType)
// case .apply(let action):
// state.text = action(state.text)
// state.valid = state.text.isValid(for: state.validationType)
case .set(let text):
state.text = text
state.valid = state.text.isValid(for: state.validationType)
@ -60,3 +59,10 @@ extension TextFieldStore {
)
}
}
extension TextFieldState {
static let placeholder = TextFieldState(
validationType: nil,
text: ""
)
}

View File

@ -18,30 +18,21 @@ typealias TransactionInputStore = Store<TransactionInputState, TransactionInputA
struct TransactionInputState: Equatable {
var textFieldState: TextFieldState
var currencySelectionState: CurrencySelectionState
var maxValue: Int64 = 0
var amount: Int64 {
Int64((Double(textFieldState.text) ?? 0.0) * 100_000_000)
}
}
enum TransactionInputAction {
case setMax(Double)
enum TransactionInputAction: Equatable {
case setMax(Int64)
case textField(TextFieldAction)
case currencySelection(CurrencySelectionAction)
}
struct TransactionInputEnvironment: Equatable {}
func maxOverride(_ reducer: @escaping (TransactionReducerData)) -> TransactionReducerData {
return { state, action in
switch action {
case .setMax(let value):
state.textFieldState.text = "\(value)"
state.currencySelectionState.currencyType = .usd
default: break
}
reducer(&state, action)
}
}
extension TransactionInputReducer {
static let `default` = TransactionInputReducer.combine(
[
@ -56,7 +47,7 @@ extension TransactionInputReducer {
switch action {
case .setMax(let value):
state.currencySelectionState.currencyType = .zec
state.textFieldState.text = "\(value)"
state.textFieldState.text = "\(value.asHumanReadableZecBalance())"
default: break
}
@ -99,3 +90,18 @@ extension TransactionInputReducer {
environment: { _ in return .init() }
)
}
extension TransactionInputState {
static let placeholer = TransactionInputState(
textFieldState: .placeholder,
currencySelectionState: CurrencySelectionState()
)
}
extension TransactionInputStore {
static let placeholder = TransactionInputStore(
initialState: .placeholer,
reducer: .default,
environment: TransactionInputEnvironment()
)
}

View File

@ -14,7 +14,7 @@ struct TransactionTextField: View {
// Constant example used here, this could be injected by a dependency
// Access to this value could also be injected into the store as a dependency
// with a function to prouce this value.
let maxTransactionValue = 500.0
let maxTransactionValue: Int64 = 500
var body: some View {
WithViewStore(store) { viewStore in
@ -68,5 +68,41 @@ struct TransactionTextField_Previews: PreviewProvider {
.padding(.horizontal, 50)
.applyScreenBackground()
.previewLayout(.fixed(width: 500, height: 200))
SingleLineTextField(
placeholderText: "$0",
title: "How much?",
store: .transaction,
titleAccessoryView: {
Button(
action: { },
label: { Text("Max") }
)
.textFieldTitleAccessoryButtonStyle
},
inputAccessoryView: {
}
)
.preferredColorScheme(.dark)
.padding(.horizontal, 50)
.applyScreenBackground()
.previewLayout(.fixed(width: 500, height: 200))
SingleLineTextField(
placeholderText: "",
title: "Address",
store: .address,
titleAccessoryView: {
},
inputAccessoryView: {
Image(Asset.Assets.Icons.qrCode.name)
.resizable()
.frame(width: 30, height: 30)
}
)
.preferredColorScheme(.dark)
.padding(.horizontal, 50)
.applyScreenBackground()
.previewLayout(.fixed(width: 500, height: 200))
}
}

View File

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

View File

@ -40,11 +40,12 @@ struct Balance: WalletBalance, Equatable {
protocol WrappedSDKSynchronizer {
var synchronizer: SDKSynchronizer? { get }
var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never> { get }
var notificationCenter: WrappedNotificationCenter { get }
func prepareWith(initializer: Initializer) throws
func start(retry: Bool) throws
func stop()
func synchronizerSynced()
func status() -> String
func getShieldedBalance() -> Effect<Balance, Never>
func getAllClearedTransactions() -> Effect<[TransactionState], Never>
@ -81,8 +82,10 @@ class LiveWrappedSDKSynchronizer: WrappedSDKSynchronizer {
private var cancellables: [AnyCancellable] = []
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never>
private(set) var notificationCenter: WrappedNotificationCenter
init() {
init(notificationCenter: WrappedNotificationCenter = .live) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(.unknown)
}
@ -93,16 +96,29 @@ class LiveWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func prepareWith(initializer: Initializer) throws {
synchronizer = try SDKSynchronizer(initializer: initializer)
NotificationCenter.default.publisher(for: .synchronizerSynced)
notificationCenter.publisherFor(.synchronizerStarted)?
.receive(on: DispatchQueue.main)
.sink(receiveValue: { [weak self] _ in
self?.synchronizerSynced()
})
.sink { [weak self] _ in self?.synchronizerStarted() }
.store(in: &cancellables)
notificationCenter.publisherFor(.synchronizerSynced)?
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.synchronizerSynced() }
.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()
}
func start(retry: Bool) throws {
try synchronizer?.start(retry: retry)
}
@ -111,10 +127,30 @@ class LiveWrappedSDKSynchronizer: WrappedSDKSynchronizer {
synchronizer?.stop()
}
func synchronizerStarted() {
stateChanged.send(.started)
}
func synchronizerSynced() {
stateChanged.send(.synced)
}
func synchronizerProgressUpdated() {
stateChanged.send(.progressUpdated)
}
func synchronizerStopped() {
stateChanged.send(.stopped)
}
func status() -> String {
guard let synchronizer = synchronizer else {
return ""
}
return SDKSynchronizer.textFor(state: synchronizer.status)
}
func getShieldedBalance() -> Effect<Balance, Never> {
if let shieldedVerifiedBalance = synchronizer?.getShieldedVerifiedBalance(),
let shieldedTotalBalance = synchronizer?.getShieldedBalance(accountIndex: 0) {
@ -215,11 +251,13 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
private var cancellables: [AnyCancellable] = []
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never>
private(set) var notificationCenter: WrappedNotificationCenter
init() {
init(notificationCenter: WrappedNotificationCenter = .mock) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(.unknown)
}
deinit {
synchronizer?.stop()
}
@ -249,6 +287,14 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
stateChanged.send(.synced)
}
func status() -> String {
guard let synchronizer = synchronizer else {
return ""
}
return SDKSynchronizer.textFor(state: synchronizer.status)
}
func getShieldedBalance() -> Effect<Balance, Never> {
return Effect(value: Balance(verified: 12345000, total: 12345000))
}
@ -339,8 +385,10 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never>
private(set) var notificationCenter: WrappedNotificationCenter
init() {
init(notificationCenter: WrappedNotificationCenter = .mock) {
self.notificationCenter = notificationCenter
self.stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(.unknown)
}
@ -352,6 +400,8 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
func synchronizerSynced() { }
func status() -> String { "" }
func getShieldedBalance() -> Effect<Balance, Never> {
return .none
}

View File

@ -19,7 +19,7 @@ class UserPreferencesStorageTests: XCTestCase {
super.setUp()
guard let userDefaults = UserDefaults.init(suiteName: "test") else {
XCTFail("UserPreferencesStorageTests: UserDefaults.init(suiteName: "test") failed to initialize")
XCTFail("UserPreferencesStorageTests: UserDefaults.init(suiteName: \"test\") failed to initialize")
return
}
@ -214,7 +214,7 @@ class UserPreferencesStorageTests: XCTestCase {
func testRemoveAll() throws {
guard let userDefaults = UserDefaults.init(suiteName: "test") else {
XCTFail("User Preferences: UserDefaults.init(suiteName: "test") failed to initialize")
XCTFail("User Preferences: UserDefaults.init(suiteName: \"test\") failed to initialize")
return
}