diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index 377f84260..5816bd42b 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -226,6 +226,7 @@ A5E22D242840C8DB00E36487 /* SafariEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E22D232840C8DB00E36487 /* SafariEngine.swift */; }; A5E22D2C2840EAC300E36487 /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E22D2B2840EAC300E36487 /* XCUIElement.swift */; }; A5E776BA29F4362D00172091 /* AlertError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E776B929F4362D00172091 /* AlertError.swift */; }; + A5F1526F2ACDC46B00D745A6 /* Web3ModalUI in Frameworks */ = {isa = PBXBuildFile; productRef = A5F1526E2ACDC46B00D745A6 /* Web3ModalUI */; }; A74D32BA2A1E25AD00CB8536 /* QueryParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74D32B92A1E25AD00CB8536 /* QueryParameters.swift */; }; C5133A78294125CC00A8314C /* Web3 in Frameworks */ = {isa = PBXBuildFile; productRef = C5133A77294125CC00A8314C /* Web3 */; }; C53AA4362941251C008EA57C /* DefaultSignerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59CF4F5292F83D50031A42F /* DefaultSignerFactory.swift */; }; @@ -729,6 +730,7 @@ C5B2F7052970573D000DBA0E /* SolanaSwift in Frameworks */, 8487A9462A836C3F0003D5AF /* Sentry in Frameworks */, C55D349929630D440004314A /* Web3Wallet in Frameworks */, + A5F1526F2ACDC46B00D745A6 /* Web3ModalUI in Frameworks */, C56EE255293F569A004840D1 /* Starscream in Frameworks */, A5B6C0F52A6EAB2800927332 /* WalletConnectNotify in Frameworks */, C56EE27B293F56F8004840D1 /* WalletConnectAuth in Frameworks */, @@ -1999,6 +2001,7 @@ A5B6C0F42A6EAB2800927332 /* WalletConnectNotify */, 84943C7C2A9BA328007EBAC2 /* Mixpanel */, A59D25ED2AB3672700D7EA3A /* AsyncButton */, + A5F1526E2ACDC46B00D745A6 /* Web3ModalUI */, ); productName = ChatWallet; productReference = C56EE21B293F55ED004840D1 /* WalletApp.app */; @@ -2076,6 +2079,7 @@ A561C7FE29DF32CE00DF540D /* XCRemoteSwiftPackageReference "HDWallet" */, 8487A9422A836C2A0003D5AF /* XCRemoteSwiftPackageReference "sentry-cocoa" */, 84943C792A9BA206007EBAC2 /* XCRemoteSwiftPackageReference "mixpanel-swift" */, + A5F1526D2ACDC46B00D745A6 /* XCRemoteSwiftPackageReference "web3modal-swift" */, ); productRefGroup = 764E1D3D26F8D3FC00A1FB15 /* Products */; projectDirPath = ""; @@ -3215,6 +3219,14 @@ version = 3.1.2; }; }; + A5F1526D2ACDC46B00D745A6 /* XCRemoteSwiftPackageReference "web3modal-swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/WalletConnect/web3modal-swift"; + requirement = { + branch = "feat/ui-package"; + kind = branch; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -3363,6 +3375,11 @@ isa = XCSwiftPackageProductDependency; productName = WalletConnectChat; }; + A5F1526E2ACDC46B00D745A6 /* Web3ModalUI */ = { + isa = XCSwiftPackageProductDependency; + package = A5F1526D2ACDC46B00D745A6 /* XCRemoteSwiftPackageReference "web3modal-swift" */; + productName = Web3ModalUI; + }; C5133A77294125CC00A8314C /* Web3 */ = { isa = XCSwiftPackageProductDependency; package = A5AE354528A1A2AC0059AE8A /* XCRemoteSwiftPackageReference "Web3" */; diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 60fae593d..7a1658ba6 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -162,6 +162,15 @@ "revision": "569255adcfff0b37e4cb8004aea29d0e2d6266df", "version": "1.0.2" } + }, + { + "package": "swift-web3modal", + "repositoryURL": "https://github.com/WalletConnect/web3modal-swift", + "state": { + "branch": "feat/ui-package", + "revision": "c87ab80f45452e1980b018bfabd8ec1f672c5ed3", + "version": null + } } ] }, diff --git a/Example/WalletApp/Common/Extensions/UIKit/UIColor.swift b/Example/WalletApp/Common/Extensions/UIKit/UIColor.swift index 1f175e66e..7d197cbef 100644 --- a/Example/WalletApp/Common/Extensions/UIKit/UIColor.swift +++ b/Example/WalletApp/Common/Extensions/UIKit/UIColor.swift @@ -17,4 +17,13 @@ extension UIColor { blue: rgb & 0xFF ) } + + func image(size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, 0) + setFill() + UIRectFill(CGRect(origin: CGPoint.zero, size: size)) + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image + } } diff --git a/Example/WalletApp/Common/VIPER/SceneViewController.swift b/Example/WalletApp/Common/VIPER/SceneViewController.swift index f8c3a97b9..33b846ed3 100644 --- a/Example/WalletApp/Common/VIPER/SceneViewController.swift +++ b/Example/WalletApp/Common/VIPER/SceneViewController.swift @@ -1,7 +1,7 @@ import SwiftUI enum NavigationBarStyle { - case translucent(UIColor) + case solid(UIColor) case clear } @@ -30,7 +30,7 @@ extension SceneViewModel { return .none } var navigationBarStyle: NavigationBarStyle { - return .translucent(.w_background) + return .solid(.w_background) } var preferredStatusBarStyle: UIStatusBarStyle { return .default @@ -56,6 +56,10 @@ class SceneViewController: UIHostingCo super.viewDidLoad() setupView() setupNavigation() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) setupNavigationBarStyle() } @@ -80,10 +84,11 @@ private extension SceneViewController { func setupNavigationBarStyle() { switch viewModel.navigationBarStyle { - case .translucent(let color): - navigationController?.navigationBar.backgroundColor = .w_background + case .solid(let color): + navigationController?.navigationBar.setBackgroundImage(color.image(), for: .default) + navigationController?.navigationBar.isTranslucent = false + navigationController?.navigationBar.backgroundColor = color navigationController?.navigationBar.barTintColor = color - navigationController?.navigationBar.isTranslucent = true case .clear: navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) navigationController?.navigationBar.shadowImage = UIImage() diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift index d2fdd1ab8..eacede968 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsInteractor.swift @@ -38,4 +38,8 @@ final class NotificationsInteractor { func unsubscribe(topic: String) async throws { try await Notify.instance.deleteSubscription(topic: topic) } + + func messagesCount(subscription: NotifySubscription) -> Int { + return Notify.instance.getMessageHistory(topic: subscription.topic).count + } } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift index fde2fb9ef..ecb906cc4 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsPresenter.swift @@ -1,5 +1,6 @@ import UIKit import Combine +import WalletConnectNotify final class NotificationsPresenter: ObservableObject { @@ -7,8 +8,24 @@ final class NotificationsPresenter: ObservableObject { private let router: NotificationsRouter private var disposeBag = Set() - @Published var subscriptions: [SubscriptionsViewModel] = [] - @Published var listings: [ListingViewModel] = [] + @Published private var subscriptions: [NotifySubscription] = [] + @Published private var listings: [Listing] = [] + + var subscriptionViewModels: [SubscriptionsViewModel] { + return subscriptions + .map { SubscriptionsViewModel(subscription: $0) } + .sorted { lhs, rhs in + return interactor.messagesCount(subscription: lhs.subscription) > interactor.messagesCount(subscription: rhs.subscription) + } + } + + var listingViewModels: [ListingViewModel] { + return listings + .map { ListingViewModel(listing: $0) } + .sorted { lhs, rhs in + return subscription(forListing: lhs) != nil && subscription(forListing: rhs) == nil + } + } init(interactor: NotificationsInteractor, router: NotificationsRouter) { defer { setupInitialState() } @@ -19,11 +36,11 @@ final class NotificationsPresenter: ObservableObject { @MainActor func fetch() async throws { - self.listings = try await interactor.getListings().map { ListingViewModel(listing: $0) } + listings = try await interactor.getListings() } func subscription(forListing listing: ListingViewModel) -> SubscriptionsViewModel? { - return subscriptions.first(where: { $0.domain == listing.appDomain }) + return subscriptionViewModels.first(where: { $0.domain == listing.appDomain }) } func subscribe(listing: ListingViewModel) async throws { @@ -50,7 +67,7 @@ final class NotificationsPresenter: ObservableObject { func removeSubscribtion(at indexSet: IndexSet) async { if let index = indexSet.first { - await interactor.removeSubscription(subscriptions[index].subscription) + await interactor.removeSubscription(subscriptionViewModels[index].subscription) } } } @@ -72,13 +89,12 @@ extension NotificationsPresenter: SceneViewModel { private extension NotificationsPresenter { func setupSubscriptions() { - self.subscriptions = interactor.getSubscriptions().map { SubscriptionsViewModel(subscription: $0) } + self.subscriptions = interactor.getSubscriptions() interactor.subscriptionsPublisher .receive(on: DispatchQueue.main) .sink { [weak self] notifySubscriptions in self?.subscriptions = notifySubscriptions - .map { SubscriptionsViewModel(subscription: $0) } } .store(in: &disposeBag) } diff --git a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift index b4c661263..a4e250e60 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/Notifications/NotificationsView.swift @@ -1,4 +1,5 @@ import SwiftUI +import Web3ModalUI import AsyncButton struct NotificationsView: View { @@ -9,37 +10,42 @@ struct NotificationsView: View { @ViewBuilder var body: some View { - VStack { - HStack { - SegmentedPicker(["Notifications", "Discover"], - selectedIndex: Binding( - get: { selectedIndex }, - set: { selectedIndex = $0 ?? 0 }), - content: { item, isSelected in - Text(item) - .font(.headline) - .foregroundColor(isSelected ? Color.primary : Color.secondary ) - .padding(.horizontal, 16) - .padding(.vertical, 8) - }, - selection: { - VStack(spacing: 0) { - Spacer() - Rectangle() - .fill(Color.primary) - .frame(height: 1) + VStack(spacing: 0) { + PreventCollapseView() + List { + Section { + if selectedIndex == 0 { + notifications() + } else { + discover() } - }) - .animation(.easeInOut(duration: 0.3)) - - Spacer() - } - - if selectedIndex == 0 { - notifications() - } else { - discover() + } header: { + HStack { + SegmentedPicker(["Notifications", "Discover"], + selectedIndex: Binding( + get: { selectedIndex }, + set: { selectedIndex = $0 ?? 0 }), + content: { item, isSelected in + Text(item) + .font(.system(size: 16, weight: .medium)) + .foregroundColor(isSelected ? Color.primary : Color.secondary ) + .padding(.trailing, 32) + .padding(.vertical, 8) + }, selection: { + VStack(spacing: 0) { + Spacer() + Rectangle() + .fill(.Blue100) + .frame(height: 2) + .padding(.trailing, 32) + } + }) + } + .animation(.easeInOut(duration: 0.3)) + .listRowBackground(Color.clear) + } } + .listStyle(PlainListStyle()) } .task { try? await presenter.fetch() @@ -47,63 +53,46 @@ struct NotificationsView: View { } private func discover() -> some View { - return List { - ForEach(presenter.listings) { listing in - discoverListRow(listing: listing) - .listRowSeparator(.hidden) - .listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 16, trailing: 16)) - .listRowBackground(Color.clear) - } + return ForEach(presenter.listingViewModels) { listing in + discoverListRow(listing: listing) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) } - .listStyle(PlainListStyle()) - .padding(.vertical, 20) } - private func notifications() -> some View { - VStack(alignment: .leading, spacing: 16) { - ZStack { - if presenter.subscriptions.isEmpty { - VStack(spacing: 10) { - Spacer() + private func emptySubscriptionsView() -> some View { + VStack(spacing: 10) { + Spacer() - Image(systemName: "bell.badge.fill") - .resizable() - .frame(width: 32, height: 32) - .aspectRatio(contentMode: .fit) - .foregroundColor(.grey50) + Image(systemName: "bell.badge.fill") + .resizable() + .frame(width: 32, height: 32) + .aspectRatio(contentMode: .fit) + .foregroundColor(.grey50) - Text("Notifications from connected apps will appear here. To enable notifications, visit the app in your browser and look for a \(Image(systemName: "bell.fill")) notifications toggle \(Image(systemName: "switch.2"))") - .foregroundColor(.grey50) - .font(.system(size: 15, weight: .regular, design: .rounded)) - .multilineTextAlignment(.center) - .lineSpacing(4) + Text("Notifications from connected apps will appear here. To enable notifications, visit the app in your browser and look for a \(Image(systemName: "bell.fill")) notifications toggle \(Image(systemName: "switch.2"))") + .foregroundColor(.grey50) + .font(.system(size: 15, weight: .regular, design: .rounded)) + .multilineTextAlignment(.center) + .lineSpacing(4) - Spacer() - } - .padding(20) - } + Spacer() + } + .padding(20) + } - VStack { - if !presenter.subscriptions.isEmpty { - List { - ForEach(presenter.subscriptions, id: \.id) { subscription in - subscriptionRow(subscription: subscription) - .listRowSeparator(.hidden) - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 16, trailing: 0)) - .listRowBackground(Color.clear) - } - .onDelete { indexSet in - Task(priority: .high) { - await presenter.removeSubscribtion(at: indexSet) - } - } - } - .listStyle(PlainListStyle()) - } - } + private func notifications() -> some View { + ForEach(presenter.subscriptionViewModels, id: \.id) { subscription in + subscriptionRow(subscription: subscription) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 20, leading: 20, bottom: 0, trailing: 20)) + } + .onDelete { indexSet in + Task(priority: .high) { + await presenter.removeSubscribtion(at: indexSet) } } - .padding(.vertical, 20) } private func subscriptionRow(subscription: SubscriptionsViewModel) -> some View { @@ -125,23 +114,16 @@ struct NotificationsView: View { .cornerRadius(30, corners: .allCorners) } } - .padding(.leading, 20) VStack(alignment: .leading, spacing: 2) { Text(subscription.name) - .foregroundColor(.grey8) - .font(.system(size: 20, weight: .semibold, design: .rounded)) + .foregroundColor(.Foreground100) + .font(.system(size: 15, weight: .medium, design: .rounded)) Text(subscription.subtitle) - .foregroundColor(.grey50) - .font(.system(size: 13, weight: .medium, design: .rounded)) + .foregroundColor(.Foreground150) + .font(.system(size: 14, weight: .regular, design: .rounded)) } - - Spacer() - - Image("forward-shevron") - .foregroundColor(.grey8) - .padding(.trailing, 20) } } } @@ -167,34 +149,39 @@ struct NotificationsView: View { Spacer() if let subscription = presenter.subscription(forListing: listing) { - AsyncButton("Unsubscribe") { + AsyncButton("Subscribed") { try await presenter.unsubscribe(subscription: subscription) } - .foregroundColor(.red) + .buttonStyle(W3MButtonStyle(size: .m, variant: .accent, rightIcon: .Checkmark)) + .disabled(true) } else { AsyncButton("Subscribe") { try await presenter.subscribe(listing: listing) } - .foregroundColor(.primary) + .buttonStyle(W3MButtonStyle(size: .m)) } } VStack(alignment: .leading, spacing: 2) { Text(listing.title) - .foregroundColor(.grey8) - .font(.system(size: 16, weight: .semibold, design: .rounded)) + .font(.paragraph700) + .foregroundColor(.Foreground100) Text(listing.appDomain ?? .empty) - .foregroundColor(.grey50) + .foregroundColor(.Foreground200) .font(.system(size: 12, weight: .medium, design: .rounded)) } Text(listing.subtitle) - .foregroundColor(.grey50) + .foregroundColor(.Foreground150) .font(.system(size: 14, weight: .regular, design: .rounded)) .lineLimit(2) } .padding(16.0) + .background( + RadialGradient(gradient: Gradient(colors: [.Blue100.opacity(0.1), .clear]), center: .topLeading, startRadius: 0, endRadius: 300) + ) + .cornerRadius(12) .overlay( RoundedRectangle(cornerRadius: 12) .stroke(Color.grey95, lineWidth: 1) @@ -202,6 +189,17 @@ struct NotificationsView: View { } } +private struct PreventCollapseView: View { + + private var mostlyClear = Color(UIColor(white: 0.0, alpha: 0.0005)) + + var body: some View { + Rectangle() + .fill(mostlyClear) + .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 1) + } +} + #if DEBUG struct NotificationsView_Previews: PreviewProvider { static var previews: some View { diff --git a/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesView.swift b/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesView.swift index bfbbfb584..764907d7a 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/NotifySettings/NotifyPreferencesView.swift @@ -21,11 +21,13 @@ struct NotifyPreferencesView: View { } .frame(maxWidth: .infinity) .alignmentGuide(.listRowSeparatorLeading) { _ in -50 } + .listRowBackground(Color.clear) ForEach(Array(viewModel.preferences.enumerated()), id: \.offset) { i, preference in if let value = viewModel.subscriptionViewModel.scope[preference] { preferenceRow(title: preference, value: value) .listRowSeparator(i == viewModel.preferences.count-1 ? .hidden : .visible) + .listRowBackground(Color.clear) } } @@ -42,6 +44,7 @@ struct NotifyPreferencesView: View { } .buttonStyle(.plain) .disabled(viewModel.isUpdateDisabled) + .listRowBackground(Color.clear) } .listStyle(.plain) } diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushMessages/SubscriptionView.swift b/Example/WalletApp/PresentationLayer/Wallet/PushMessages/SubscriptionView.swift index b4e99af8f..2a37713ef 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/PushMessages/SubscriptionView.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/PushMessages/SubscriptionView.swift @@ -5,23 +5,29 @@ struct SubscriptionView: View { @EnvironmentObject var presenter: SubscriptionPresenter var body: some View { - VStack(spacing: 0) { + ZStack { + VStack { + RadialGradient(gradient: Gradient(colors: [.Blue100.opacity(0.1), .clear]), center: .topLeading, startRadius: 0, endRadius: 300) + .frame(height: 300) + Spacer() + } + List { headerView() .listRowSeparator(.hidden) .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets()) if !presenter.messages.isEmpty { ForEach(presenter.messages, id: \.id) { pm in notificationView(pushMessage: pm) - .listRowSeparator(.hidden) - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 32, trailing: 0)) + .listRowSeparator(.visible) + .listRowInsets(EdgeInsets(top: 16, leading: 0, bottom: 16, trailing: 0)) .listRowBackground(Color.clear) } .onDelete { indexSet in presenter.deletePushMessage(at: indexSet) } - Spacer().frame(height: 50.0) } else { emptyStateView() .listRowSeparator(.hidden) @@ -31,11 +37,12 @@ struct SubscriptionView: View { .listStyle(PlainListStyle()) } .ignoresSafeArea(.container) + .safeAreaInset(edge: .bottom) { Spacer().frame(height: 50) } } private func notificationView(pushMessage: NotifyMessageViewModel) -> some View { VStack(alignment: .center) { - HStack(spacing: 10) { + HStack(spacing: 12) { CacheAsyncImage(url: URL(string: pushMessage.imageUrl)) { phase in if let image = phase.image { image @@ -54,18 +61,18 @@ struct SubscriptionView: View { VStack(alignment: .leading, spacing: 2) { HStack { Text(pushMessage.title) - .foregroundColor(.primary) + .foregroundColor(.Foreground100) .font(.system(size: 14, weight: .semibold)) Spacer() Text(pushMessage.publishedAt) - .foregroundColor(.grey50) + .foregroundColor(.Foreground250) .font(.system(size: 11)) } Text(pushMessage.subtitle) - .foregroundColor(.grey50) + .foregroundColor(.Foreground175) .font(.system(size: 13)) } @@ -87,22 +94,22 @@ struct SubscriptionView: View { } } .clipShape(Circle()) - .padding(.top, 50.0) + .padding(.top, 56.0) .padding(.bottom, 8.0) Text(presenter.subscriptionViewModel.name) - .font(.headline) - .foregroundColor(.primary) + .font(.large700) + .foregroundColor(.Foreground100) .padding(.bottom, 8.0) Text(presenter.subscriptionViewModel.domain) - .font(.caption) - .foregroundColor(.secondary) + .font(.system(size: 12, weight: .medium)) + .foregroundColor(.Foreground200) .padding(.bottom, 16.0) Text(presenter.subscriptionViewModel.description) - .font(.footnote) - .foregroundColor(.primary) + .font(.system(size: 14, weight: .regular)) + .foregroundColor(.Foreground100) .padding(.bottom, 16.0) Menu { @@ -127,6 +134,7 @@ struct SubscriptionView: View { .stroke(Color.grey95, lineWidth: 1) ) } + .padding(.bottom, 20.0) } .frame(maxWidth: .infinity) } diff --git a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift index 31da26d27..47ffbceec 100644 --- a/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift +++ b/Sources/WalletConnectNotify/Client/Wallet/NotifyClient.swift @@ -82,11 +82,20 @@ public class NotifyClient { return try await withCheckedThrowingContinuation { continuation in var cancellable: AnyCancellable? - cancellable = subscriptionsPublisher.sink { subscriptions in - guard subscriptions.contains(where: { $0.metadata.url == appDomain }) else { return } - cancellable?.cancel() - continuation.resume(with: .success(())) - } + cancellable = subscriptionsPublisher + .setFailureType(to: Error.self) + .timeout(10, scheduler: RunLoop.main, customError: { Errors.subscribeTimeout }) + .sink(receiveCompletion: { completion in + defer { cancellable?.cancel() } + switch completion { + case .failure(let error): continuation.resume(with: .failure(error)) + case .finished: break + } + }, receiveValue: { subscriptions in + guard subscriptions.contains(where: { $0.metadata.url == appDomain }) else { return } + cancellable?.cancel() + continuation.resume(with: .success(())) + }) Task { [cancellable] in do { @@ -132,6 +141,20 @@ public class NotifyClient { } } +private extension NotifyClient { + + enum Errors: Error, LocalizedError { + case subscribeTimeout + + var errorDescription: String? { + switch self { + case .subscribeTimeout: + return "Subscribe method timeout" + } + } + } +} + #if targetEnvironment(simulator) extension NotifyClient { public func register(deviceToken: String) async throws {