2020-06-24 19:26:14 -07:00
//
// C o m b i n e S y n c h r o n i z e r . s w i f t
// w a l l e t
//
// C r e a t e d b y F r a n c i s c o G i n d r e o n 1 / 2 7 / 2 0 .
// C o p y r i g h t © 2 0 2 0 F r a n c i s c o G i n d r e . A l l r i g h t s r e s e r v e d .
//
import Foundation
import Combine
import ZcashLightClientKit
class CombineSynchronizer {
var initializer : Initializer {
synchronizer . initializer
}
2020-07-01 16:01:12 -07:00
var synchronizer : SDKSynchronizer
2020-06-24 19:26:14 -07:00
var status : CurrentValueSubject < Status , Never >
var progress : CurrentValueSubject < Float , Never >
var syncBlockHeight : CurrentValueSubject < BlockHeight , Never >
var minedTransaction = PassthroughSubject < PendingTransactionEntity , Never > ( )
var balance : CurrentValueSubject < Double , Never >
var verifiedBalance : CurrentValueSubject < Double , Never >
var cancellables = [ AnyCancellable ] ( )
var error = PassthroughSubject < Error , Never > ( )
var receivedTransactions : Future < [ ConfirmedTransactionEntity ] , Never > {
Future < [ ConfirmedTransactionEntity ] , Never > ( ) {
promise in
DispatchQueue . global ( ) . async {
[ weak self ] in
guard let self = self else {
promise ( . success ( [ ] ) )
return
}
promise ( . success ( self . synchronizer . receivedTransactions ) )
}
}
}
var sentTransactions : Future < [ ConfirmedTransactionEntity ] , Never > {
Future < [ ConfirmedTransactionEntity ] , Never > ( ) {
promise in
DispatchQueue . global ( ) . async {
[ weak self ] in
guard let self = self else {
promise ( . success ( [ ] ) )
return
}
promise ( . success ( self . synchronizer . sentTransactions ) )
}
}
}
var pendingTransactions : Future < [ PendingTransactionEntity ] , Never > {
Future < [ PendingTransactionEntity ] , Never > ( ) {
[ weak self ] promise in
guard let self = self else {
promise ( . success ( [ ] ) )
return
}
DispatchQueue . global ( ) . async {
promise ( . success ( self . synchronizer . pendingTransactions ) )
}
}
}
init ( initializer : Initializer ) throws {
self . synchronizer = try SDKSynchronizer ( initializer : initializer )
self . status = CurrentValueSubject ( . disconnected )
self . progress = CurrentValueSubject ( 0 )
self . balance = CurrentValueSubject ( 0 )
self . verifiedBalance = CurrentValueSubject ( 0 )
self . syncBlockHeight = CurrentValueSubject ( ZcashSDK . SAPLING_ACTIVATION_HEIGHT )
NotificationCenter . default . publisher ( for : . synchronizerSynced ) . sink ( receiveValue : { _ in
self . balance . send ( initializer . getBalance ( ) . asHumanReadableZecBalance ( ) )
self . verifiedBalance . send ( initializer . getVerifiedBalance ( ) . asHumanReadableZecBalance ( ) )
} ) . store ( in : & cancellables )
NotificationCenter . default . publisher ( for : . synchronizerStarted ) . sink { _ in
self . status . send ( . syncing )
} . store ( in : & cancellables )
NotificationCenter . default . publisher ( for : . synchronizerProgressUpdated ) . receive ( on : DispatchQueue . main ) . sink ( receiveValue : { ( progressNotification ) in
guard let newProgress = progressNotification . userInfo ? [ SDKSynchronizer . NotificationKeys . progress ] as ? Float else { return }
self . progress . send ( newProgress )
guard let blockHeight = progressNotification . userInfo ? [ SDKSynchronizer . NotificationKeys . blockHeight ] as ? BlockHeight else { return }
self . syncBlockHeight . send ( blockHeight )
} ) . store ( in : & cancellables )
NotificationCenter . default . publisher ( for : . synchronizerMinedTransaction ) . sink ( receiveValue : { minedNotification in
guard let minedTx = minedNotification . userInfo ? [ SDKSynchronizer . NotificationKeys . minedTransaction ] as ? PendingTransactionEntity else { return }
self . minedTransaction . send ( minedTx )
} ) . store ( in : & cancellables )
NotificationCenter . default . publisher ( for : . synchronizerFailed ) . sink { ( notification ) in
guard let error = notification . userInfo ? [ SDKSynchronizer . NotificationKeys . error ] as ? Error else {
self . error . send ( ZirclesEnvironment . WalletError . genericError ( message : " An error ocurred, but we can't figure out what it is. Please check device logs for more details " )
)
return
}
self . error . send ( error )
} . store ( in : & cancellables )
}
func start ( retry : Bool = false ) {
do {
if retry {
stop ( )
}
try synchronizer . start ( retry : retry )
} catch {
logger . error ( " error starting \( error ) " )
}
}
func stop ( ) {
do {
try synchronizer . stop ( )
} catch {
logger . error ( " error stopping \( error ) " )
}
}
func cancel ( pendingTransaction : PendingTransactionEntity ) -> Bool {
synchronizer . cancelSpend ( transaction : pendingTransaction )
}
deinit {
for c in cancellables {
c . cancel ( )
}
}
func send ( with spendingKey : String , zatoshi : Int64 , to recipientAddress : String , memo : String ? , from account : Int ) -> Future < PendingTransactionEntity , Error > {
Future < PendingTransactionEntity , Error > ( ) {
promise in
self . synchronizer . sendToAddress ( spendingKey : spendingKey , zatoshi : zatoshi , toAddress : recipientAddress , memo : memo , from : account ) { ( result ) in
switch result {
case . failure ( let error ) :
promise ( . failure ( error ) )
case . success ( let pendingTx ) :
promise ( . success ( pendingTx ) )
}
}
}
}
2020-06-30 17:47:50 -07:00
func latestHeight ( ) -> Future < BlockHeight , Error > {
Future < BlockHeight , Error > ( ) { promise in
DispatchQueue . global ( ) . async { [ weak self ] in
guard let self = self else { return }
do {
promise ( . success ( try self . initializer . lightWalletService . latestBlockHeight ( ) ) )
} catch {
promise ( . failure ( error ) )
}
}
}
}
2020-06-24 19:26:14 -07:00
}
2020-06-30 17:47:50 -07:00