WrappedSyncrhonizer (#292)

(un)subscribed logic added
cleanup
refactor
commented solved
final agreed naming
This commit is contained in:
Lukas Korba 2022-04-26 13:28:39 +02:00 committed by GitHub
parent 7e7ba0d11d
commit ccb9301fb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 66 deletions

View File

@ -106,7 +106,7 @@
9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppReducerTests.swift */; };
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; };
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */; };
9EAFEB882806E5AE00199FC9 /* CombineSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB872806E5AE00199FC9 /* CombineSynchronizer.swift */; };
9EAFEB882806E5AE00199FC9 /* WrappedSDKSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */; };
9EAFEB8A2806F48100199FC9 /* ZCashSDKEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB892806F48100199FC9 /* ZCashSDKEnvironment.swift */; };
9EAFEB8F2808183D00199FC9 /* SandboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB8D2808183D00199FC9 /* SandboxView.swift */; };
9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */; };
@ -269,7 +269,7 @@
9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = "<group>"; };
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = "<group>"; };
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = "<group>"; };
9EAFEB872806E5AE00199FC9 /* CombineSynchronizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineSynchronizer.swift; sourceTree = "<group>"; };
9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSDKSynchronizer.swift; sourceTree = "<group>"; };
9EAFEB892806F48100199FC9 /* ZCashSDKEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZCashSDKEnvironment.swift; sourceTree = "<group>"; };
9EAFEB8D2808183D00199FC9 /* SandboxView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxView.swift; sourceTree = "<group>"; };
9EAFEB8E2808183D00199FC9 /* SandboxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxStore.swift; sourceTree = "<group>"; };
@ -561,7 +561,6 @@
9EF8139B27F47AED0075AF48 /* InitializationState.swift */,
9EF8139027F191BF0075AF48 /* WalletStorageInteractor.swift */,
9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */,
9EAFEB872806E5AE00199FC9 /* CombineSynchronizer.swift */,
9EAFEB892806F48100199FC9 /* ZCashSDKEnvironment.swift */,
9E2F1C8128095AFE004E65FE /* Int64+Zcash.swift */,
9E2F1C832809B606004E65FE /* DebugMenu.swift */,
@ -749,6 +748,7 @@
9E02B56927FED43E005B809B /* WrappedFileManager.swift */,
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */,
9E02B5C2280458D2005B809B /* WrappedDerivationTool.swift */,
9EAFEB872806E5AE00199FC9 /* WrappedSDKSynchronizer.swift */,
);
path = Wrappers;
sourceTree = "<group>";
@ -1225,7 +1225,7 @@
0DF482BA2787ADA800EB37D6 /* ConditionalModifier.swift in Sources */,
9E2F1C8F280EDE09004E65FE /* Drawer.swift in Sources */,
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
9EAFEB882806E5AE00199FC9 /* CombineSynchronizer.swift in Sources */,
9EAFEB882806E5AE00199FC9 /* WrappedSDKSynchronizer.swift in Sources */,
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,
F9971A4D27680DC400A2DB75 /* App.swift in Sources */,
9EAFEB9228081E9400199FC9 /* HomeView.swift in Sources */,

View File

@ -41,7 +41,7 @@ enum AppAction: Equatable {
}
struct AppEnvironment {
let combineSynchronizer: CombineSynchronizer
let wrappedSDKSynchronizer: WrappedSDKSynchronizer
let databaseFiles: DatabaseFilesInteractor
let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider
let scheduler: AnySchedulerOf<DispatchQueue>
@ -52,7 +52,7 @@ struct AppEnvironment {
extension AppEnvironment {
static let live = AppEnvironment(
combineSynchronizer: LiveCombineSynchronizer(),
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer(),
databaseFiles: .live(),
mnemonicSeedPhraseProvider: .live,
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
@ -62,7 +62,7 @@ extension AppEnvironment {
)
static let mock = AppEnvironment(
combineSynchronizer: LiveCombineSynchronizer(),
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer(),
databaseFiles: .live(),
mnemonicSeedPhraseProvider: .mock,
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
@ -153,8 +153,8 @@ extension AppReducer {
birthday: birthday,
with: environment
)
try environment.combineSynchronizer.prepareWith(initializer: initializer)
try environment.combineSynchronizer.start()
try environment.wrappedSDKSynchronizer.prepareWith(initializer: initializer)
try environment.wrappedSDKSynchronizer.start()
} catch {
state.appInitializationState = .failed
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
@ -291,7 +291,7 @@ extension AppReducer {
action: /AppAction.home,
environment: { environment in
HomeEnvironment(
combineSynchronizer: environment.combineSynchronizer
wrappedSDKSynchronizer: environment.wrappedSDKSynchronizer
)
}
)

View File

@ -9,7 +9,6 @@ struct HomeState: Equatable {
case scan
}
var arePublishersPrepared = false
var route: Route?
var drawerOverlay: DrawerOverlay
@ -24,11 +23,13 @@ struct HomeState: Equatable {
enum HomeAction: Equatable {
case debugMenuStartup
case preparePublishers
case onAppear
case onDisappear
case profile(ProfileAction)
case request(RequestAction)
case send(SendAction)
case scan(ScanAction)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case transactionHistory(TransactionHistoryAction)
case updateBalance(Balance)
case updateDrawer(DrawerOverlay)
@ -36,26 +37,35 @@ enum HomeAction: Equatable {
}
struct HomeEnvironment {
let combineSynchronizer: CombineSynchronizer
let wrappedSDKSynchronizer: WrappedSDKSynchronizer
}
// MARK: - HomeReducer
private struct ListenerId: Hashable {}
typealias HomeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
extension HomeReducer {
static let `default` = HomeReducer { state, action, environment in
switch action {
case .preparePublishers:
if !state.arePublishersPrepared {
state.arePublishersPrepared = true
return environment.combineSynchronizer.shieldedBalance
.receive(on: DispatchQueue.main)
.map({ Balance(verified: $0.verified, total: $0.total) })
.map(HomeAction.updateBalance)
.eraseToEffect()
}
case .onAppear:
return environment.wrappedSDKSynchronizer.stateChanged
.map(HomeAction.synchronizerStateChanged)
.eraseToEffect()
.cancellable(id: ListenerId(), cancelInFlight: true)
case .onDisappear:
return Effect.cancel(id: ListenerId())
case .synchronizerStateChanged(.synced):
return environment.wrappedSDKSynchronizer.getShieldedBalance()
.receive(on: DispatchQueue.main)
.map({ Balance(verified: $0.verified, total: $0.total) })
.map(HomeAction.updateBalance)
.eraseToEffect()
case .synchronizerStateChanged(let synchronizerState):
return .none
case .updateBalance(let balance):

View File

@ -41,7 +41,8 @@ struct HomeView: View {
}
.applyScreenBackground()
.navigationBarHidden(true)
.onAppear(perform: { viewStore.send(.preparePublishers) })
.onAppear(perform: { viewStore.send(.onAppear) })
.onDisappear(perform: { viewStore.send(.onDisappear) })
}
}
}
@ -160,7 +161,7 @@ extension HomeStore {
initialState: .placeholder,
reducer: .default.debug(),
environment: HomeEnvironment(
combineSynchronizer: LiveCombineSynchronizer()
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer()
)
)
}

View File

@ -1,5 +1,5 @@
//
// CombineSynchronizer.swift
// WrappedSDKSynchronizer.swift
// secant-testnet
//
// Created by Lukáš Korba on 13.04.2022.
@ -8,26 +8,51 @@
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
}
struct Balance: WalletBalance, Equatable {
var verified: Int64
var total: Int64
}
protocol CombineSynchronizer {
protocol WrappedSDKSynchronizer {
var synchronizer: SDKSynchronizer? { get }
var shieldedBalance: CurrentValueSubject<WalletBalance, Never> { get }
var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never> { get }
func prepareWith(initializer: Initializer) throws
func start(retry: Bool) throws
func stop()
func updatePublishers()
func synchronizerSynced()
func getShieldedBalance() -> Effect<Balance, Never>
func getTransparentAddress(account: Int) -> TransparentAddress?
func getShieldedAddress(account: Int) -> SaplingShieldedAddress?
}
extension CombineSynchronizer {
extension WrappedSDKSynchronizer {
func start() throws {
try start(retry: false)
}
@ -41,13 +66,17 @@ extension CombineSynchronizer {
}
}
class LiveCombineSynchronizer: CombineSynchronizer {
class LiveWrappedSDKSynchronizer: WrappedSDKSynchronizer {
private var cancellables: [AnyCancellable] = []
private(set) var synchronizer: SDKSynchronizer?
private(set) var shieldedBalance: CurrentValueSubject<WalletBalance, Never>
private(set) var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never>
init() {
self.shieldedBalance = CurrentValueSubject<WalletBalance, Never>(Balance(verified: 0, total: 0))
self.stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(.unknown)
}
deinit {
synchronizer?.stop()
}
func prepareWith(initializer: Initializer) throws {
@ -56,7 +85,7 @@ class LiveCombineSynchronizer: CombineSynchronizer {
NotificationCenter.default.publisher(for: .synchronizerSynced)
.receive(on: DispatchQueue.main)
.sink(receiveValue: { [weak self] _ in
self?.updatePublishers()
self?.synchronizerSynced()
})
.store(in: &cancellables)
@ -71,15 +100,19 @@ class LiveCombineSynchronizer: CombineSynchronizer {
synchronizer?.stop()
}
func updatePublishers() {
func synchronizerSynced() {
stateChanged.send(.synced)
}
func getShieldedBalance() -> Effect<Balance, Never> {
if let shieldedVerifiedBalance = synchronizer?.getShieldedVerifiedBalance(),
let shieldedTotalBalance = synchronizer?.getShieldedBalance(accountIndex: 0) {
shieldedBalance.send(Balance(verified: shieldedVerifiedBalance, total: shieldedTotalBalance))
} else {
shieldedBalance.send(Balance(verified: 0, total: 0))
return Effect(value: Balance(verified: shieldedVerifiedBalance, total: shieldedTotalBalance))
}
return .none
}
func getTransparentAddress(account: Int) -> TransparentAddress? {
synchronizer?.getTransparentAddress(accountIndex: account)
}
@ -89,38 +122,27 @@ class LiveCombineSynchronizer: CombineSynchronizer {
}
}
class MockCombineSynchronizer: CombineSynchronizer {
class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
private(set) var synchronizer: SDKSynchronizer?
private(set) var shieldedBalance: CurrentValueSubject<WalletBalance, Never>
private(set) var stateChanged: CurrentValueSubject<WrappedSDKSynchronizerState, Never>
init() {
self.shieldedBalance = CurrentValueSubject<WalletBalance, Never>(Balance(verified: 0, total: 0))
self.stateChanged = CurrentValueSubject<WrappedSDKSynchronizerState, Never>(.unknown)
}
func prepareWith(initializer: Initializer) throws {
synchronizer = try SDKSynchronizer(initializer: initializer)
shieldedBalance = CurrentValueSubject<WalletBalance, Never>(
Balance(verified: 0, total: 0)
)
try synchronizer?.prepare()
}
func prepareWith(initializer: Initializer) throws { }
func start(retry: Bool) throws {
try synchronizer?.start(retry: retry)
}
func start(retry: Bool) throws { }
func stop() {
synchronizer?.stop()
}
func stop() { }
func updatePublishers() {
}
func synchronizerSynced() { }
func getTransparentAddress(account: Int) -> TransparentAddress? {
synchronizer?.getTransparentAddress(accountIndex: account)
func getShieldedBalance() -> Effect<Balance, Never> {
return .none
}
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? {
synchronizer?.getShieldedAddress(accountIndex: account)
}
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? { nil }
}

View File

@ -13,7 +13,7 @@ class AppReducerTests: XCTestCase {
static let testScheduler = DispatchQueue.test
let testEnvironment = AppEnvironment(
combineSynchronizer: MockCombineSynchronizer(),
wrappedSDKSynchronizer: MockWrappedSDKSynchronizer(),
databaseFiles: .throwing,
mnemonicSeedPhraseProvider: .mock,
scheduler: testScheduler.eraseToAnyScheduler(),
@ -24,7 +24,7 @@ class AppReducerTests: XCTestCase {
func testWalletInitializationState_Uninitialized() throws {
let uninitializedEnvironment = AppEnvironment(
combineSynchronizer: MockCombineSynchronizer(),
wrappedSDKSynchronizer: MockWrappedSDKSynchronizer(),
databaseFiles: .throwing,
mnemonicSeedPhraseProvider: .mock,
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
@ -46,7 +46,7 @@ class AppReducerTests: XCTestCase {
)
let keysMissingEnvironment = AppEnvironment(
combineSynchronizer: MockCombineSynchronizer(),
wrappedSDKSynchronizer: MockWrappedSDKSynchronizer(),
databaseFiles: .live(databaseFiles: DatabaseFiles(fileManager: wfmMock)),
mnemonicSeedPhraseProvider: .mock,
scheduler: Self.testScheduler.eraseToAnyScheduler(),
@ -68,7 +68,7 @@ class AppReducerTests: XCTestCase {
)
let keysMissingEnvironment = AppEnvironment(
combineSynchronizer: MockCombineSynchronizer(),
wrappedSDKSynchronizer: MockWrappedSDKSynchronizer(),
databaseFiles: .live(databaseFiles: DatabaseFiles(fileManager: wfmMock)),
mnemonicSeedPhraseProvider: .mock,
scheduler: Self.testScheduler.eraseToAnyScheduler(),