diff --git a/Tangem/App/Services/UserTokensManager/CommonUserTokensManager.swift b/Tangem/App/Services/UserTokensManager/CommonUserTokensManager.swift index aba35fc1ad..870b0a5c13 100644 --- a/Tangem/App/Services/UserTokensManager/CommonUserTokensManager.swift +++ b/Tangem/App/Services/UserTokensManager/CommonUserTokensManager.swift @@ -23,7 +23,9 @@ class CommonUserTokensManager { private let derivationStyle: DerivationStyle? private let existingCurves: [EllipticCurve] private let longHashesSupported: Bool - private weak var keysDerivingProvider: KeysDerivingProvider? + + weak var keysDerivingProvider: KeysDerivingProvider? + private var pendingUserTokensSyncCompletions: [() -> Void] = [] private var bag: Set = [] @@ -34,7 +36,6 @@ class CommonUserTokensManager { walletModelsManager: WalletModelsManager, derivationStyle: DerivationStyle?, derivationManager: DerivationManager?, - keysDerivingProvider: KeysDerivingProvider, existingCurves: [EllipticCurve], longHashesSupported: Bool ) { @@ -44,7 +45,6 @@ class CommonUserTokensManager { self.walletModelsManager = walletModelsManager self.derivationStyle = derivationStyle self.derivationManager = derivationManager - self.keysDerivingProvider = keysDerivingProvider self.existingCurves = existingCurves self.longHashesSupported = longHashesSupported } diff --git a/Tangem/App/Services/UserWalletRepository/CommonUserWalletRepository.swift b/Tangem/App/Services/UserWalletRepository/CommonUserWalletRepository.swift index d6a8645bee..6cff7c8f75 100644 --- a/Tangem/App/Services/UserWalletRepository/CommonUserWalletRepository.swift +++ b/Tangem/App/Services/UserWalletRepository/CommonUserWalletRepository.swift @@ -106,7 +106,7 @@ class CommonUserWalletRepository: UserWalletRepository { names: models.map(\.name) ) - let userWalletModel = CommonUserWalletModel(cardInfo: cardInfo) + let userWalletModel = CommonUserWalletModelFactory().makeModel(cardInfo: cardInfo) if let userWalletModel { initializeServices(for: userWalletModel) } @@ -498,7 +498,7 @@ class CommonUserWalletRepository: UserWalletRepository { migrateNamesIfNeeded(&savedUserWallets) models = savedUserWallets.map { userWalletStorageItem in - if let userWallet = CommonUserWalletModel(userWallet: userWalletStorageItem) { + if let userWallet = CommonUserWalletModelFactory().makeModel(userWallet: userWalletStorageItem) { return userWallet } else { return LockedUserWalletModel(with: userWalletStorageItem) @@ -511,7 +511,7 @@ class CommonUserWalletRepository: UserWalletRepository { guard let index = models.firstIndex(where: { $0.userWalletId == userWalletId }) else { return } guard let savedUserWallet = savedUserWallet(with: userWalletId), - let userWalletModel = CommonUserWalletModel(userWallet: savedUserWallet) else { + let userWalletModel = CommonUserWalletModelFactory().makeModel(userWallet: savedUserWallet) else { return } diff --git a/Tangem/App/ViewModels/CommonUserWalletModel.swift b/Tangem/App/ViewModels/CommonUserWalletModel.swift index e9358fe545..e323ca8b9b 100644 --- a/Tangem/App/ViewModels/CommonUserWalletModel.swift +++ b/Tangem/App/ViewModels/CommonUserWalletModel.swift @@ -21,42 +21,17 @@ class CommonUserWalletModel { @Injected(\.pushNotificationsInteractor) private var pushNotificationsInteractor: PushNotificationsInteractor let walletModelsManager: WalletModelsManager - - var userTokensManager: UserTokensManager { _userTokensManager } - - private lazy var _userTokensManager = CommonUserTokensManager( - userWalletId: userWalletId, - shouldLoadSwapAvailability: config.isFeatureVisible(.swapping), - userTokenListManager: userTokenListManager, - walletModelsManager: walletModelsManager, - derivationStyle: config.derivationStyle, - derivationManager: derivationManager, - keysDerivingProvider: self, - existingCurves: config.existingCurves, - longHashesSupported: config.hasFeature(.longHashes) - ) - + let userTokensManager: UserTokensManager let userTokenListManager: UserTokenListManager let keysRepository: KeysRepository + let derivationManager: DerivationManager? + let totalBalanceProvider: TotalBalanceProviding + private let walletManagersRepository: WalletManagersRepository private let cardImageProvider = CardImageProvider() private var associatedCardIds: Set - lazy var derivationManager: DerivationManager? = { - guard config.hasFeature(.hdWallets) else { - return nil - } - - let commonDerivationManager = CommonDerivationManager( - keysRepository: keysRepository, - userTokenListManager: userTokenListManager - ) - - commonDerivationManager.delegate = self - return commonDerivationManager - }() - var signer: TangemSigner { _signer } var cardId: String { cardInfo.card.cardId } @@ -77,12 +52,6 @@ class CommonUserWalletModel { let userWalletId: UserWalletId - lazy var totalBalanceProvider: TotalBalanceProviding = TotalBalanceProvider( - userWalletId: userWalletId, - walletModelsManager: walletModelsManager, - derivationManager: derivationManager - ) - private(set) var cardInfo: CardInfo private var tangemSdk: TangemSdk? var config: UserWalletConfig @@ -99,44 +68,34 @@ class CommonUserWalletModel { } } - convenience init?(userWallet: StoredUserWallet) { - let cardInfo = userWallet.cardInfo() - self.init(cardInfo: cardInfo) - let allIds = associatedCardIds.union(userWallet.associatedCardIds) - associatedCardIds = allIds - } - - init?(cardInfo: CardInfo) { - let config = UserWalletConfigFactory(cardInfo).makeConfig() - associatedCardIds = [cardInfo.card.cardId] - guard let userWalletIdSeed = config.userWalletIdSeed, - let walletManagerFactory = try? config.makeAnyWalletManagerFactory() else { - return nil - } - + init( + cardInfo: CardInfo, + config: UserWalletConfig, + userWalletId: UserWalletId, + associatedCardIds: Set, + walletManagersRepository: WalletManagersRepository, + walletModelsManager: WalletModelsManager, + userTokensManager: CommonUserTokensManager, + userTokenListManager: UserTokenListManager, + keysRepository: KeysRepository, + derivationManager: DerivationManager?, + totalBalanceProvider: TotalBalanceProviding + ) { self.cardInfo = cardInfo self.config = config - keysRepository = CommonKeysRepository(with: cardInfo.card.wallets) - - userWalletId = UserWalletId(with: userWalletIdSeed) - userTokenListManager = CommonUserTokenListManager( - userWalletId: userWalletId.value, - supportedBlockchains: config.supportedBlockchains, - hdWalletsSupported: config.hasFeature(.hdWallets), - hasTokenSynchronization: config.hasFeature(.tokenSynchronization), - defaultBlockchains: config.defaultBlockchains - ) - - walletManagersRepository = CommonWalletManagersRepository( - keysProvider: keysRepository, - userTokenListManager: userTokenListManager, - walletManagerFactory: walletManagerFactory - ) - - walletModelsManager = CommonWalletModelsManager( - walletManagersRepository: walletManagersRepository, - walletModelsFactory: config.makeWalletModelsFactory() - ) + self.userWalletId = userWalletId + + var associatedCardIds = associatedCardIds + associatedCardIds.insert(cardInfo.card.cardId) + self.associatedCardIds = associatedCardIds + + self.walletManagersRepository = walletManagersRepository + self.walletModelsManager = walletModelsManager + self.userTokensManager = userTokensManager + self.userTokenListManager = userTokenListManager + self.keysRepository = keysRepository + self.derivationManager = derivationManager + self.totalBalanceProvider = totalBalanceProvider _signer = config.tangemSigner _userWalletNamePublisher = .init(cardInfo.name) diff --git a/Tangem/App/ViewModels/CommonUserWalletModelFactory.swift b/Tangem/App/ViewModels/CommonUserWalletModelFactory.swift new file mode 100644 index 0000000000..2b355da9d7 --- /dev/null +++ b/Tangem/App/ViewModels/CommonUserWalletModelFactory.swift @@ -0,0 +1,97 @@ +// +// CommonUserWalletModelFactory.swift +// Tangem +// +// Created by Alexander Osokin on 24.09.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation + +struct CommonUserWalletModelFactory { + func makeModel(userWallet: StoredUserWallet) -> CommonUserWalletModel? { + let cardInfo = userWallet.cardInfo() + return makeModel(cardInfo: cardInfo, associatedCardIds: userWallet.associatedCardIds) + } + + func makeModel(cardInfo: CardInfo, associatedCardIds: Set = []) -> CommonUserWalletModel? { + let config = UserWalletConfigFactory(cardInfo).makeConfig() + + guard let userWalletIdSeed = config.userWalletIdSeed, + let walletManagerFactory = try? config.makeAnyWalletManagerFactory() else { + return nil + } + + let userWalletId = UserWalletId(with: userWalletIdSeed) + + let keysRepository = CommonKeysRepository(with: cardInfo.card.wallets) + + let userTokenListManager = CommonUserTokenListManager( + userWalletId: userWalletId.value, + supportedBlockchains: config.supportedBlockchains, + hdWalletsSupported: config.hasFeature(.hdWallets), + hasTokenSynchronization: config.hasFeature(.tokenSynchronization), + defaultBlockchains: config.defaultBlockchains + ) + + let walletManagersRepository = CommonWalletManagersRepository( + keysProvider: keysRepository, + userTokenListManager: userTokenListManager, + walletManagerFactory: walletManagerFactory + ) + + let walletModelsManager = CommonWalletModelsManager( + walletManagersRepository: walletManagersRepository, + walletModelsFactory: config.makeWalletModelsFactory() + ) + + let derivationManager: CommonDerivationManager? = { + guard config.hasFeature(.hdWallets) else { + return nil + } + + let commonDerivationManager = CommonDerivationManager( + keysRepository: keysRepository, + userTokenListManager: userTokenListManager + ) + + return commonDerivationManager + }() + + let totalBalanceProvider = TotalBalanceProvider( + userWalletId: userWalletId, + walletModelsManager: walletModelsManager, + derivationManager: derivationManager + ) + + let userTokensManager = CommonUserTokensManager( + userWalletId: userWalletId, + shouldLoadSwapAvailability: config.isFeatureVisible(.swapping), + userTokenListManager: userTokenListManager, + walletModelsManager: walletModelsManager, + derivationStyle: config.derivationStyle, + derivationManager: derivationManager, + existingCurves: config.existingCurves, + longHashesSupported: config.hasFeature(.longHashes) + ) + + let model = CommonUserWalletModel( + cardInfo: cardInfo, + config: config, + userWalletId: userWalletId, + associatedCardIds: associatedCardIds, + walletManagersRepository: walletManagersRepository, + walletModelsManager: walletModelsManager, + userTokensManager: userTokensManager, + userTokenListManager: userTokenListManager, + keysRepository: keysRepository, + derivationManager: derivationManager, + totalBalanceProvider: totalBalanceProvider + ) + + derivationManager?.delegate = model + userTokensManager.keysDerivingProvider = model + + return model + } +} diff --git a/Tangem/Common/Preview/PreviewCard.swift b/Tangem/Common/Preview/PreviewCard.swift index b39f9f555f..1721001a9f 100644 --- a/Tangem/Common/Preview/PreviewCard.swift +++ b/Tangem/Common/Preview/PreviewCard.swift @@ -26,7 +26,7 @@ enum PreviewCard { var userWalletModel: CommonUserWalletModel { let card = CardDTO(card: card) let ci = CardInfo(card: card, walletData: walletData, name: "Name") - let vm = CommonUserWalletModel(cardInfo: ci)! + let vm = CommonUserWalletModelFactory().makeModel(cardInfo: ci)! if let blockchain = blockchain { let factory = WalletManagerFactory( config: .init( diff --git a/Tangem/Common/UI/RefreshableScrollView.swift b/Tangem/Common/UI/RefreshableScrollView.swift index 45ce8c93ea..a634c0a930 100644 --- a/Tangem/Common/UI/RefreshableScrollView.swift +++ b/Tangem/Common/UI/RefreshableScrollView.swift @@ -14,15 +14,15 @@ typealias OnRefresh = (_ completionHandler: @escaping RefreshCompletionHandler) /// Author: The SwiftUI Lab. /// Full article: https://swiftui-lab.com/scrollview-pull-to-refresh/. struct RefreshableScrollView: View { - let onRefresh: OnRefresh let content: Content + private let refreshContainer: RefreshContainer init( onRefresh: @escaping OnRefresh, @ViewBuilder content: () -> Content ) { - self.onRefresh = onRefresh self.content = content() + refreshContainer = RefreshContainer(onRefresh: onRefresh) } var body: some View { @@ -30,20 +30,11 @@ struct RefreshableScrollView: View { ScrollView(.vertical, showsIndicators: false) { content } - .refreshable { - await refreshAsync() + .refreshable { [weak refreshContainer] in + await refreshContainer?.refreshAsync() } } else { - RefreshableScrollViewCompat(onRefresh: onRefresh, content: content) - } - } - - @MainActor - private func refreshAsync() async { - await withCheckedContinuation { continuation in - onRefresh { - continuation.resume() - } + RefreshableScrollViewCompat(onRefresh: refreshContainer.onRefresh, content: content) } } } @@ -167,6 +158,27 @@ private struct RefreshableScrollViewCompat: View { } } +// MARK: - RefreshContainer + +extension RefreshableScrollView { + private final class RefreshContainer { + let onRefresh: OnRefresh + + init(onRefresh: @escaping OnRefresh) { + self.onRefresh = onRefresh + } + + @MainActor + func refreshAsync() async { + await withCheckedContinuation { continuation in + onRefresh { + continuation.resume() + } + } + } + } +} + // MARK: - Previews struct RefreshableScrollViewView_Previews: PreviewProvider { diff --git a/Tangem/Modules/Main/MultiWalletMainContent/MultiWalletMainContentViewModel.swift b/Tangem/Modules/Main/MultiWalletMainContent/MultiWalletMainContentViewModel.swift index de452ec67e..fe2b52411c 100644 --- a/Tangem/Modules/Main/MultiWalletMainContent/MultiWalletMainContentViewModel.swift +++ b/Tangem/Modules/Main/MultiWalletMainContent/MultiWalletMainContentViewModel.swift @@ -275,6 +275,7 @@ final class MultiWalletMainContentViewModel: ObservableObject { var tokenListSyncSubscription: AnyCancellable? tokenListSyncSubscription = Publishers.Zip(tokenListSyncPublisher, sectionsPublisher) + .prefix(1) .receive(on: DispatchQueue.main) .withWeakCaptureOf(self) .sink { viewModel, _ in diff --git a/Tangem/Modules/Onboarding/BaseModels/OnboardingViewModel.swift b/Tangem/Modules/Onboarding/BaseModels/OnboardingViewModel.swift index 1b66f47dc0..8b29d7f6eb 100644 --- a/Tangem/Modules/Onboarding/BaseModels/OnboardingViewModel.swift +++ b/Tangem/Modules/Onboarding/BaseModels/OnboardingViewModel.swift @@ -203,7 +203,7 @@ class OnboardingViewModel } func initializeUserWallet(from cardInfo: CardInfo) { - guard let userWallet = CommonUserWalletModel(cardInfo: cardInfo) else { return } + guard let userWallet = CommonUserWalletModelFactory().makeModel(cardInfo: cardInfo) else { return } userWalletRepository.initializeServices(for: userWallet) diff --git a/Tangem/Preview Content/Mocks/Common/CommonUserWalletModel+Mock.swift b/Tangem/Preview Content/Mocks/Common/CommonUserWalletModel+Mock.swift index daa9cf59cf..d5219fa0ff 100644 --- a/Tangem/Preview Content/Mocks/Common/CommonUserWalletModel+Mock.swift +++ b/Tangem/Preview Content/Mocks/Common/CommonUserWalletModel+Mock.swift @@ -9,7 +9,7 @@ import Foundation extension CommonUserWalletModel { - static let mock = CommonUserWalletModel( + static let mock = CommonUserWalletModelFactory().makeModel( cardInfo: CardMock.wallet.cardInfo ) } diff --git a/TangemApp.xcodeproj/project.pbxproj b/TangemApp.xcodeproj/project.pbxproj index 52fdd2e2d6..0520aa4b9c 100644 --- a/TangemApp.xcodeproj/project.pbxproj +++ b/TangemApp.xcodeproj/project.pbxproj @@ -1084,6 +1084,7 @@ DC0A584A282C07A20031BECC /* WalletConnectService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0A5849282C07A20031BECC /* WalletConnectService.swift */; }; DC0A584C282C07B10031BECC /* CommonWalletConnectService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0A584B282C07B10031BECC /* CommonWalletConnectService.swift */; }; DC0A5856282E3E220031BECC /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0A5855282E3E220031BECC /* ServicesManager.swift */; }; + DC0BA52A2CA2E112009D9233 /* CommonUserWalletModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0BA5292CA2E112009D9233 /* CommonUserWalletModelFactory.swift */; }; DC0CC5B22B0D3B0900E32CAC /* UserWalletIdPreflightReadFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0CC5B12B0D3B0900E32CAC /* UserWalletIdPreflightReadFilter.swift */; }; DC0CC5B42B0D3F0E00E32CAC /* TwinPreflightReadFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0CC5B32B0D3F0E00E32CAC /* TwinPreflightReadFilter.swift */; }; DC0DE2722B60392900E7247F /* SafariManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0DE2712B60392900E7247F /* SafariManager.swift */; }; @@ -3069,6 +3070,7 @@ DC0A5849282C07A20031BECC /* WalletConnectService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnectService.swift; sourceTree = ""; }; DC0A584B282C07B10031BECC /* CommonWalletConnectService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonWalletConnectService.swift; sourceTree = ""; }; DC0A5855282E3E220031BECC /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = ""; }; + DC0BA5292CA2E112009D9233 /* CommonUserWalletModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonUserWalletModelFactory.swift; sourceTree = ""; }; DC0CC5B12B0D3B0900E32CAC /* UserWalletIdPreflightReadFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserWalletIdPreflightReadFilter.swift; sourceTree = ""; }; DC0CC5B32B0D3F0E00E32CAC /* TwinPreflightReadFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwinPreflightReadFilter.swift; sourceTree = ""; }; DC0DE2712B60392900E7247F /* SafariManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariManager.swift; sourceTree = ""; }; @@ -4353,6 +4355,7 @@ 5DFCCB7724C389740014A293 /* CommonUserWalletModel.swift */, 5D39516025597DB1002CC452 /* BalanceViewModel.swift */, B0A2B3E825D6B5BD0001EEB4 /* TokenBalanceViewModel.swift */, + DC0BA5292CA2E112009D9233 /* CommonUserWalletModelFactory.swift */, ); path = ViewModels; sourceTree = ""; @@ -11091,6 +11094,7 @@ EF7A072E2C2F0738003DED83 /* SendTransactionDispatcher.swift in Sources */, DCC5C99C2C136D8F001B970A /* PushNotificationsPermissionRequestDelegate.swift in Sources */, B0EA18AD2C465D860023B72A /* DescriptionBottomSheetView.swift in Sources */, + DC0BA52A2CA2E112009D9233 /* CommonUserWalletModelFactory.swift in Sources */, B04719012B4E7C4800444734 /* VisaBalancesLimitsBottomSheetViewModel.swift in Sources */, DC70C0172B95EEC3002205BA /* FactorySettingsResetting.swift in Sources */, EF729B712AF3E86300D80205 /* ExpressProvidersSelectorView.swift in Sources */,