diff --git a/Example/DApp/AppDelegate.swift b/Example/DApp/AppDelegate.swift index ccfb15c17..acc22e14a 100644 --- a/Example/DApp/AppDelegate.swift +++ b/Example/DApp/AppDelegate.swift @@ -8,6 +8,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + func applicationWillEnterForeground(_ application: UIApplication) { + ProfilingService.instance.send(logMessage: .init(message: "AppDelegate: application will enter foreground")) + // Additional code to handle entering the foreground + } + + func applicationDidBecomeActive(_ application: UIApplication) { + ProfilingService.instance.send(logMessage: .init(message: "AppDelegate: application did become active")) + // Additional code to handle becoming active + } + // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { @@ -17,6 +27,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) async -> Bool { + ProfilingService.instance.send(logMessage: .init(message: "AppDelegate: will try to dispatch envelope: \(String(describing: userActivity.webpageURL))")) guard let url = userActivity.webpageURL, let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return true @@ -32,4 +43,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + // Log the event of opening the app via URL + ProfilingService.instance.send(logMessage: .init(message: "AppDelegate: app opened by URL: \(url.absoluteString)")) + + // Handle the URL appropriately + try! Sign.instance.dispatchEnvelope(url.absoluteString) + + return true + } } diff --git a/Example/DApp/Common/InputConfig.swift b/Example/DApp/Common/InputConfig.swift deleted file mode 100644 index d710282b7..000000000 --- a/Example/DApp/Common/InputConfig.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Foundation - -struct InputConfig { - - static var projectId: String { - guard let projectId = config(for: "PROJECT_ID"), !projectId.isEmpty else { - fatalError("PROJECT_ID is either not defined or empty in Configuration.xcconfig") - } - - return projectId - } - - private static func config(for key: String) -> String? { - return Bundle.main.object(forInfoDictionaryKey: key) as? String - } -} diff --git a/Example/DApp/Info.plist b/Example/DApp/Info.plist index 20e5043da..1f05fe57e 100644 --- a/Example/DApp/Info.plist +++ b/Example/DApp/Info.plist @@ -2,6 +2,8 @@ + MIXPANEL_TOKEN + $(MIXPANEL_TOKEN) CFBundleURLTypes diff --git a/Example/DApp/Modules/Configuration/ConfigPresenter.swift b/Example/DApp/Modules/Configuration/ConfigPresenter.swift index b210d33fd..0872c7c25 100644 --- a/Example/DApp/Modules/Configuration/ConfigPresenter.swift +++ b/Example/DApp/Modules/Configuration/ConfigPresenter.swift @@ -8,6 +8,11 @@ final class ConfigPresenter: ObservableObject, SceneViewModel { private let router: ConfigRouter + var clientId: String { + guard let clientId = try? Networking.interactor.getClientId() else { return .empty } + return clientId + } + init( router: ConfigRouter ) { diff --git a/Example/DApp/Modules/Configuration/ConfigView.swift b/Example/DApp/Modules/Configuration/ConfigView.swift index 3c20e8441..35d061ddf 100644 --- a/Example/DApp/Modules/Configuration/ConfigView.swift +++ b/Example/DApp/Modules/Configuration/ConfigView.swift @@ -2,6 +2,8 @@ import SwiftUI struct ConfigView: View { @EnvironmentObject var presenter: ConfigPresenter + @State private var copyAlert: Bool = false + @State private var cacheCleanAlert: Bool = false var body: some View { NavigationStack { @@ -10,22 +12,71 @@ struct ConfigView: View { .ignoresSafeArea() ScrollView { - VStack { - Button { + VStack(spacing: 12) { + // Clean Cache Button + Button(action: { presenter.cleanLinkModeSupportedWalletsCache() - } label: { - HStack { - Spacer() - Text("Clean Link Mode Supported Wallets Cache") - .font(.system(size: 16, weight: .semibold)) - .foregroundColor(.white) - .padding(.vertical, 25) - Spacer() + cacheCleanAlert = true + }) { + VStack(alignment: .leading, spacing: 4) { + HStack(spacing: 6) { + Text("Clean Cache") + .multilineTextAlignment(.leading) + .foregroundColor(.white) + .font(.system(size: 16, weight: .semibold)) + + Image(systemName: "trash") + .foregroundColor(.white) + + Spacer() + } + .padding(.horizontal, 12) + .padding(.top, 16) + + Text("Clean link mode supported wallets cache") + .multilineTextAlignment(.leading) + .foregroundColor(.white.opacity(0.7)) + .font(.system(size: 14)) + .padding(.horizontal, 12) + .padding(.bottom, 16) } - .background(Color(red: 95/255, green: 159/255, blue: 248/255)) - .cornerRadius(16) + .background(Color(red: 95/255, green: 159/255, blue: 248/255).opacity(0.2).cornerRadius(12)) } + .frame(maxWidth: .infinity) + .padding(.horizontal, 12) .padding(.top, 10) + + // Client ID Row + Button(action: { + UIPasteboard.general.string = presenter.clientId + copyAlert = true + }) { + VStack(alignment: .leading, spacing: 4) { + HStack(spacing: 6) { + Text("Client ID") + .multilineTextAlignment(.leading) + .foregroundColor(.white) + .font(.system(size: 16, weight: .semibold)) + + Image(systemName: "doc.on.doc") + .foregroundColor(.white) + + Spacer() + } + .padding(.horizontal, 12) + .padding(.top, 16) + + Text(presenter.clientId) + .multilineTextAlignment(.leading) + .foregroundColor(.white.opacity(0.7)) + .font(.system(size: 14)) + .padding(.horizontal, 12) + .padding(.bottom, 16) + } + .background(Color(red: 95/255, green: 159/255, blue: 248/255).opacity(0.2).cornerRadius(12)) + } + .frame(maxWidth: .infinity) + .padding(.horizontal, 12) } .padding(12) } @@ -43,6 +94,12 @@ struct ConfigView: View { for: .navigationBar ) } + .alert("Cache cleaned successfully", isPresented: $cacheCleanAlert) { + Button("OK", role: .cancel) { } + } + .alert("Client ID copied to clipboard", isPresented: $copyAlert) { + Button("OK", role: .cancel) { } + } } } diff --git a/Example/DApp/Modules/Sign/SignPresenter.swift b/Example/DApp/Modules/Sign/SignPresenter.swift index f21cc84e2..0b2e86e7d 100644 --- a/Example/DApp/Modules/Sign/SignPresenter.swift +++ b/Example/DApp/Modules/Sign/SignPresenter.swift @@ -52,7 +52,7 @@ final class SignPresenter: ObservableObject { } Web3Modal.present(from: nil) } - + func connectWalletWithWCM() { WalletConnectModal.set(sessionParams: .init( requiredNamespaces: Proposal.requiredNamespaces, @@ -158,14 +158,6 @@ final class SignPresenter: ObservableObject { // MARK: - Private functions extension SignPresenter { private func setupInitialState() { - Sign.instance.sessionSettlePublisher - .receive(on: DispatchQueue.main) - .sink { [unowned self] _ in - self.router.dismiss() - self.getSession() - } - .store(in: &subscriptions) - getSession() Sign.instance.sessionDeletePublisher @@ -184,6 +176,9 @@ extension SignPresenter { case .success(let (session, _)): if session == nil { AlertPresenter.present(message: "Wallet Succesfully Authenticated", type: .success) + } else { + self.router.dismiss() + self.getSession() } break case .failure(let error): @@ -200,13 +195,6 @@ extension SignPresenter { } .store(in: &subscriptions) - Sign.instance.sessionsPublisher - .receive(on: DispatchQueue.main) - .sink { [unowned self] _ in - self.router.dismiss() - self.getSession() - } - .store(in: &subscriptions) Sign.instance.requestExpirationPublisher .receive(on: DispatchQueue.main) .sink { _ in @@ -214,6 +202,20 @@ extension SignPresenter { AlertPresenter.present(message: "Session Request has expired", type: .warning) } .store(in: &subscriptions) + + Web3Modal.instance.SIWEAuthenticationPublisher + .receive(on: DispatchQueue.main) + .sink { [unowned self] result in + switch result { + case .success((let message, let signature)): + AlertPresenter.present(message: "Authenticated with SIWE", type: .success) + self.router.dismiss() + self.getSession() + case .failure(let error): + AlertPresenter.present(message: "\(error)", type: .warning) + } + } + .store(in: &subscriptions) } private func getSession() { diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index 2de2c9534..2153d26f3 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -15,6 +15,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + ProfilingService.instance.send(logMessage: .init(message: "SceneDelegate: will try to dispatch envelope - userActivity: \(String(describing: userActivity.webpageURL))")) guard let url = userActivity.webpageURL, let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return @@ -27,12 +28,73 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + configureClientsIfNeeded() + setUpProfilingIfNeeded() + ProfilingService.instance.send(logMessage: .init(message: "SceneDelegate: willConnectTo : \(String(describing: connectionOptions.userActivities.first?.webpageURL?.absoluteString))")) + + configureClientsIfNeeded() + setUpProfilingIfNeeded() + + + setupWindow(scene: scene) + } + + private func setupWindow(scene: UIScene) { + guard let windowScene = (scene as? UIWindowScene) else { return } + window = UIWindow(windowScene: windowScene) + + let viewController = SignModule.create(app: app) + .wrapToNavigationController() + + window?.rootViewController = viewController + window?.makeKeyAndVisible() + } + + func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { + ProfilingService.instance.send(logMessage: .init(message: "SceneDelegate: - openURLContexts : \(String(describing: URLContexts.first?.url))")) + + guard let context = URLContexts.first else { return } + + let url = context.url + + guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), + let queryItems = components.queryItems, + queryItems.contains(where: { $0.name == "wc_ev" }) else { + return + } + + do { + try Sign.instance.dispatchEnvelope(url.absoluteString) + } catch { + AlertPresenter.present(message: error.localizedDescription, type: .error) + } + } + + func sceneWillEnterForeground(_ scene: UIScene) { + ProfilingService.instance.send(logMessage: .init(message: "SceneDelegate: scene will enter foreground")) + // Additional code to handle entering the foreground + } + + func sceneDidBecomeActive(_ scene: UIScene) { + ProfilingService.instance.send(logMessage: .init(message: "SceneDelegate: scene did become active")) + // Additional code to handle becoming active + } + + private func setUpProfilingIfNeeded() { + if let clientId = try? Networking.interactor.getClientId() { + ProfilingService.instance.setUpProfiling(account: "swift_dapp_\(clientId)", clientId: clientId) + } + } + + var clientsConfigured = false + private func configureClientsIfNeeded() { + if clientsConfigured {return} + else {clientsConfigured = true} Networking.configure( groupIdentifier: Constants.groupIdentifier, projectId: InputConfig.projectId, socketFactory: DefaultSocketFactory() ) - Sign.configure(crypto: DefaultCryptoProvider()) let metadata = AppMetadata( name: "Swift Dapp", @@ -41,7 +103,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { icons: ["https://avatars.githubusercontent.com/u/37784886"], redirect: try! AppMetadata.Redirect(native: "wcdapp://", universal: "https://lab.web3modal.com/dapp", linkMode: true) ) - + Web3Modal.configure( projectId: InputConfig.projectId, metadata: metadata, @@ -72,7 +134,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { projectId: InputConfig.projectId, metadata: metadata ) - + Sign.instance.logger.setLogging(level: .debug) Networking.instance.setLogging(level: .debug) @@ -94,17 +156,5 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { }.store(in: &publishers) Web3Modal.instance.disableAnalytics() - setupWindow(scene: scene) - } - - private func setupWindow(scene: UIScene) { - guard let windowScene = (scene as? UIWindowScene) else { return } - window = UIWindow(windowScene: windowScene) - - let viewController = SignModule.create(app: app) - .wrapToNavigationController() - - window?.rootViewController = viewController - window?.makeKeyAndVisible() } } diff --git a/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan b/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan index 3e624e54b..a7e0d1b30 100644 --- a/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan +++ b/Example/ExampleApp.xcodeproj/IntegrationTests.xctestplan @@ -10,6 +10,11 @@ ], "defaultOptions" : { "codeCoverage" : false, + "commandLineArgumentEntries" : [ + { + "argument" : "isTesting" + } + ], "environmentVariableEntries" : [ { "key" : "RELAY_HOST", diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj index b7d71d28a..2447d9efa 100644 --- a/Example/ExampleApp.xcodeproj/project.pbxproj +++ b/Example/ExampleApp.xcodeproj/project.pbxproj @@ -24,6 +24,9 @@ 846E35A42C0065B600E63DF4 /* ConfigPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846E35A32C0065B600E63DF4 /* ConfigPresenter.swift */; }; 846E35A62C0065C100E63DF4 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846E35A52C0065C100E63DF4 /* ConfigView.swift */; }; 846E35A82C006C5600E63DF4 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846E35A72C006C5600E63DF4 /* Constants.swift */; }; + 84733CD32C1C2A4B001B2850 /* AlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AEC2502B4D42C100E27A5B /* AlertPresenter.swift */; }; + 84733CD42C1C2C24001B2850 /* ProfilingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50B6A372B06697B00162B01 /* ProfilingService.swift */; }; + 84733CD52C1C2CEB001B2850 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56EE25D293F56D6004840D1 /* InputConfig.swift */; }; 847BD1D62989492500076C90 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D12989492500076C90 /* MainViewController.swift */; }; 847BD1D82989492500076C90 /* MainModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D32989492500076C90 /* MainModule.swift */; }; 847BD1D92989492500076C90 /* MainPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D42989492500076C90 /* MainPresenter.swift */; }; @@ -36,7 +39,6 @@ 847BD1E8298A806800076C90 /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1E3298A806800076C90 /* NotificationsView.swift */; }; 847BD1EB298A87AB00076C90 /* SubscriptionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1EA298A87AB00076C90 /* SubscriptionsViewModel.swift */; }; 847F08012A25DBFF00B2A5A4 /* XPlatformW3WTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847F08002A25DBFF00B2A5A4 /* XPlatformW3WTests.swift */; }; - 8486EDD12B4F2DC1008E53C3 /* AlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486EDD02B4F2DC1008E53C3 /* AlertPresenter.swift */; }; 8486EDD32B4F2EA6008E53C3 /* SwiftMessages in Frameworks */ = {isa = PBXBuildFile; productRef = 8486EDD22B4F2EA6008E53C3 /* SwiftMessages */; }; 8487A9442A836C2A0003D5AF /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 8487A9432A836C2A0003D5AF /* Sentry */; }; 8487A9462A836C3F0003D5AF /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 8487A9452A836C3F0003D5AF /* Sentry */; }; @@ -87,7 +89,6 @@ A518A98829683FB60035247E /* Web3InboxModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518A98529683FB60035247E /* Web3InboxModule.swift */; }; A518A98929683FB60035247E /* Web3InboxRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518A98629683FB60035247E /* Web3InboxRouter.swift */; }; A518B31428E33A6500A2CE93 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518B31328E33A6500A2CE93 /* InputConfig.swift */; }; - A51AC0D928E436A3001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0D828E436A3001BACF9 /* InputConfig.swift */; }; A51AC0DF28E4379F001BACF9 /* InputConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51AC0DE28E4379F001BACF9 /* InputConfig.swift */; }; A5417BBE299BFC3E00B469F3 /* ImportAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5417BBD299BFC3E00B469F3 /* ImportAccount.swift */; }; A541959E2934BFEF0035AD19 /* CacaoSignerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A541959A2934BFEF0035AD19 /* CacaoSignerTests.swift */; }; @@ -419,7 +420,6 @@ 847BD1E3298A806800076C90 /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = ""; }; 847BD1EA298A87AB00076C90 /* SubscriptionsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsViewModel.swift; sourceTree = ""; }; 847F08002A25DBFF00B2A5A4 /* XPlatformW3WTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPlatformW3WTests.swift; sourceTree = ""; }; - 8486EDD02B4F2DC1008E53C3 /* AlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPresenter.swift; sourceTree = ""; }; 8487A92E2A7BD2F30003D5AF /* XPlatformProtocolTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = XPlatformProtocolTests.xctestplan; path = ../XPlatformProtocolTests.xctestplan; sourceTree = ""; }; 8487A9472A83AD680003D5AF /* LoggingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingService.swift; sourceTree = ""; }; 849A4F18298281E300E61ACE /* WalletAppRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WalletAppRelease.entitlements; sourceTree = ""; }; @@ -470,7 +470,6 @@ A518A98529683FB60035247E /* Web3InboxModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Web3InboxModule.swift; sourceTree = ""; }; A518A98629683FB60035247E /* Web3InboxRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Web3InboxRouter.swift; sourceTree = ""; }; A518B31328E33A6500A2CE93 /* InputConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; - A51AC0D828E436A3001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A51AC0DE28E4379F001BACF9 /* InputConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputConfig.swift; sourceTree = ""; }; A5417BBD299BFC3E00B469F3 /* ImportAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportAccount.swift; sourceTree = ""; }; A541959A2934BFEF0035AD19 /* CacaoSignerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacaoSignerTests.swift; sourceTree = ""; }; @@ -1380,6 +1379,8 @@ 84CB43D429B9FC88004DDA31 /* Tests */, A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */, A59CF4F5292F83D50031A42F /* DefaultSignerFactory.swift */, + A50B6A372B06697B00162B01 /* ProfilingService.swift */, + 84AEC2502B4D42C100E27A5B /* AlertPresenter.swift */, A5A0843B29D2F60A000B9B17 /* DefaultCryptoProvider.swift */, A51606F72A2F47BD00CACB92 /* DefaultBIP44Provider.swift */, A5417BBD299BFC3E00B469F3 /* ImportAccount.swift */, @@ -1392,10 +1393,8 @@ A5BB7FAB28B6AA7100707FC6 /* Common */ = { isa = PBXGroup; children = ( - A51AC0D828E436A3001BACF9 /* InputConfig.swift */, A5BB7FAC28B6AA7D00707FC6 /* QRCodeGenerator.swift */, 84D093EA2B4EA6CB005B1925 /* ActivityIndicatorManager.swift */, - 8486EDD02B4F2DC1008E53C3 /* AlertPresenter.swift */, ); path = Common; sourceTree = ""; @@ -1630,7 +1629,6 @@ C56EE262293F56D6004840D1 /* Extensions */, C56EE263293F56D6004840D1 /* VIPER */, 84AEC24E2B4D1EE400E27A5B /* ActivityIndicatorManager.swift */, - 84AEC2502B4D42C100E27A5B /* AlertPresenter.swift */, ); path = Common; sourceTree = ""; @@ -1688,7 +1686,6 @@ C56EE280293F5757004840D1 /* Application.swift */, C56EE27F293F5757004840D1 /* AppDelegate.swift */, C56EE281293F5757004840D1 /* SceneDelegate.swift */, - A50B6A372B06697B00162B01 /* ProfilingService.swift */, 84DB38F22983CDAE00BFEE37 /* PushRegisterer.swift */, A51811972A52E21A00A52B15 /* ConfigurationService.swift */, 8487A9472A83AD680003D5AF /* LoggingService.swift */, @@ -2249,6 +2246,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 84733CD52C1C2CEB001B2850 /* InputConfig.swift in Sources */, + 84733CD42C1C2C24001B2850 /* ProfilingService.swift in Sources */, + 84733CD32C1C2A4B001B2850 /* AlertPresenter.swift in Sources */, C5BE021C2AF79B9A0064FC88 /* SessionAccountRouter.swift in Sources */, C5B4C4C42AF11C8B00B4274A /* SignView.swift in Sources */, C5BE02042AF7764F0064FC88 /* UIViewController.swift in Sources */, @@ -2262,7 +2262,6 @@ 846E359F2C00654F00E63DF4 /* ConfigModule.swift in Sources */, C5BE01E52AF697470064FC88 /* Color.swift in Sources */, A5BB7FAD28B6AA7D00707FC6 /* QRCodeGenerator.swift in Sources */, - A51AC0D928E436A3001BACF9 /* InputConfig.swift in Sources */, C5BE01E22AF693080064FC88 /* Application.swift in Sources */, C5BE021B2AF79B9A0064FC88 /* SessionAccountPresenter.swift in Sources */, A5A8E47E293A1CFE00FEB97D /* DefaultSignerFactory.swift in Sources */, @@ -2273,7 +2272,6 @@ C5BE02012AF774CB0064FC88 /* NewPairingModule.swift in Sources */, C5BE02002AF774CB0064FC88 /* NewPairingRouter.swift in Sources */, C5BE01F82AF6CB270064FC88 /* SignInteractor.swift in Sources */, - 8486EDD12B4F2DC1008E53C3 /* AlertPresenter.swift in Sources */, C5BE01D12AF661D70064FC88 /* NewPairingView.swift in Sources */, 846E35A42C0065B600E63DF4 /* ConfigPresenter.swift in Sources */, 84CE642127981DED00142511 /* SceneDelegate.swift in Sources */, @@ -3280,7 +3278,7 @@ repositoryURL = "https://github.com/WalletConnect/web3modal-swift"; requirement = { kind = revision; - revision = 3baac675811b5fdeb689cef703e3dfc7682704e6; + revision = 25abd7e5471f21d662400f9763948fbf97c60c97; }; }; A5434021291E6A270068F706 /* XCRemoteSwiftPackageReference "solana-swift" */ = { diff --git a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3198e81b5..53859947e 100644 --- a/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ExampleApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -186,7 +186,7 @@ "repositoryURL": "https://github.com/WalletConnect/web3modal-swift", "state": { "branch": null, - "revision": "3baac675811b5fdeb689cef703e3dfc7682704e6", + "revision": "25abd7e5471f21d662400f9763948fbf97c60c97", "version": null } } diff --git a/Example/DApp/Common/AlertPresenter.swift b/Example/Shared/AlertPresenter.swift similarity index 100% rename from Example/DApp/Common/AlertPresenter.swift rename to Example/Shared/AlertPresenter.swift diff --git a/Example/WalletApp/ApplicationLayer/ProfilingService.swift b/Example/Shared/ProfilingService.swift similarity index 90% rename from Example/WalletApp/ApplicationLayer/ProfilingService.swift rename to Example/Shared/ProfilingService.swift index 709f42cc7..c5c3ee119 100644 --- a/Example/WalletApp/ApplicationLayer/ProfilingService.swift +++ b/Example/Shared/ProfilingService.swift @@ -2,7 +2,7 @@ import Foundation import Mixpanel import WalletConnectNetworking import Combine -import Web3Wallet +import WalletConnectSign import WalletConnectNotify final class ProfilingService { @@ -33,10 +33,7 @@ final class ProfilingService { mixpanel.people.set(properties: ["$name": account, "account": account]) handleLogs(from: Networking.instance.logsPublisher) - handleLogs(from: Notify.instance.logsPublisher) - handleLogs(from: Push.instance.logsPublisher) - handleLogs(from: Web3Wallet.instance.logsPublisher) - + handleLogs(from: Sign.instance.logsPublisher) } private func handleLogs(from publisher: AnyPublisher) { diff --git a/Example/WalletApp/Common/AlertPresenter.swift b/Example/WalletApp/Common/AlertPresenter.swift deleted file mode 100644 index 5da5d4668..000000000 --- a/Example/WalletApp/Common/AlertPresenter.swift +++ /dev/null @@ -1,35 +0,0 @@ -import Foundation -import SwiftMessages -import UIKit - -struct AlertPresenter { - enum MessageType { - case warning - case error - case info - case success - } - - static func present(message: String, type: AlertPresenter.MessageType) { - DispatchQueue.main.async { - let view = MessageView.viewFromNib(layout: .cardView) - switch type { - case .warning: - view.configureTheme(.warning, iconStyle: .subtle) - case .error: - view.configureTheme(.error, iconStyle: .subtle) - case .info: - view.configureTheme(.info, iconStyle: .subtle) - case .success: - view.configureTheme(.success, iconStyle: .subtle) - } - view.button?.isHidden = true - view.layoutMarginAdditions = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20) - view.configureContent(title: "", body: message) - var config = SwiftMessages.Config() - config.presentationStyle = .top - config.duration = .seconds(seconds: 1.5) - SwiftMessages.show(config: config, view: view) - } - } -} diff --git a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift index ce57ddd3d..fdd623c2f 100644 --- a/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift +++ b/Example/WalletApp/PresentationLayer/Wallet/AuthRequest/AuthRequestPresenter.swift @@ -87,7 +87,7 @@ final class AuthRequestPresenter: ObservableObject { /* Redirect */ if let uri = request.requester.redirect?.native { - WalletConnectRouter.goBack(uri: uri) +// WalletConnectRouter.goBack(uri: uri) router.dismiss() } else { showSignedSheet.toggle() diff --git a/README.md b/README.md index e07ccaa25..2048a8dce 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Wallet Connect v.2 - Swift +# WalletConnect v.2 - Swift ![CI main](https://github.com/WalletConnect/WalletConnectSwiftV2/actions/workflows/ci.yml/badge.svg?branch=main) ![CI develop](https://github.com/WalletConnect/WalletConnectSwiftV2/actions/workflows/ci.yml/badge.svg?branch=develop) @@ -58,4 +58,4 @@ Apache 2.0 ## Guides -- [Artifacts sometimes not available in Actions -> Build name -> Artifacts?](./docs/guides/downloading_artifacts.md) \ No newline at end of file +- [Artifacts sometimes not available in Actions -> Build name -> Artifacts?](./docs/guides/downloading_artifacts.md) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 803999dce..b9ab60bd3 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.19.2"} +{"version": "1.19.3"} diff --git a/Sources/WalletConnectSign/Auth/Link/LinkEnvelopesDispatcher.swift b/Sources/WalletConnectSign/Auth/Link/LinkEnvelopesDispatcher.swift index e79b9354f..bd7d093a1 100644 --- a/Sources/WalletConnectSign/Auth/Link/LinkEnvelopesDispatcher.swift +++ b/Sources/WalletConnectSign/Auth/Link/LinkEnvelopesDispatcher.swift @@ -7,6 +7,7 @@ final class LinkEnvelopesDispatcher { case invalidURL case envelopeNotFound case topicNotFound + case failedToOpenUniversalLink(String) } private let serializer: Serializing private let logger: ConsoleLogging @@ -36,15 +37,18 @@ final class LinkEnvelopesDispatcher { } func dispatchEnvelope(_ envelope: String) throws { + logger.debug("will dispatch an envelope: \(envelope)") guard let envelopeURL = URL(string: envelope), let components = URLComponents(url: envelopeURL, resolvingAgainstBaseURL: true) else { throw Errors.invalidURL } guard let wcEnvelope = components.queryItems?.first(where: { $0.name == "wc_ev" })?.value else { + logger.error(Errors.envelopeNotFound.localizedDescription) throw Errors.envelopeNotFound } guard let topic = components.queryItems?.first(where: { $0.name == "topic" })?.value else { + logger.error(Errors.topicNotFound.localizedDescription) throw Errors.topicNotFound } manageEnvelope(topic, wcEnvelope) @@ -52,16 +56,34 @@ final class LinkEnvelopesDispatcher { func request(topic: String, request: RPCRequest, peerUniversalLink: String, envelopeType: Envelope.EnvelopeType) async throws -> String { + logger.debug("Will send request with link mode") try rpcHistory.set(request, forTopic: topic, emmitedBy: .local, transportType: .relay) let envelopeUrl: URL do { envelopeUrl = try serializeAndCreateUrl(peerUniversalLink: peerUniversalLink, encodable: request, envelopeType: envelopeType, topic: topic) - DispatchQueue.main.async { - UIApplication.shared.open(envelopeUrl, options: [.universalLinksOnly: true]) + logger.debug("Will try to open envelopeUrl: \(envelopeUrl)") + + try await withCheckedThrowingContinuation { continuation in + if isRunningTests() { + continuation.resume(returning: ()) + return + } + DispatchQueue.main.async { [weak self] in + self?.logger.debug("Will open universal link") + UIApplication.shared.open(envelopeUrl, options: [.universalLinksOnly: true]) { success in + if success { + continuation.resume(returning: ()) + } else { + continuation.resume(throwing: Errors.failedToOpenUniversalLink(envelopeUrl.absoluteString)) + } + } + } } + } catch { + logger.error("Failed to open url, error: \(error) ") if let id = request.id { rpcHistory.delete(id: id) } @@ -72,17 +94,35 @@ final class LinkEnvelopesDispatcher { } func respond(topic: String, response: RPCResponse, peerUniversalLink: String, envelopeType: Envelope.EnvelopeType) async throws -> String { + logger.debug("will redpond for a request id: \(String(describing: response.id))") try rpcHistory.validate(response) let envelopeUrl = try serializeAndCreateUrl(peerUniversalLink: peerUniversalLink, encodable: response, envelopeType: envelopeType, topic: topic) - DispatchQueue.main.async { - UIApplication.shared.open(envelopeUrl, options: [.universalLinksOnly: true]) + logger.debug("Prepared envelopeUrl: \(envelopeUrl)") + + try await withCheckedThrowingContinuation { continuation in + if isRunningTests() { + continuation.resume(returning: ()) + return + } + DispatchQueue.main.async { [unowned self] in + logger.debug("Will open universal link") + UIApplication.shared.open(envelopeUrl, options: [.universalLinksOnly: true]) { success in + if success { + continuation.resume(returning: ()) + } else { + continuation.resume(throwing: Errors.failedToOpenUniversalLink(envelopeUrl.absoluteString)) + } + } + } } + try rpcHistory.resolve(response) return envelopeUrl.absoluteString } public func respondError(topic: String, requestId: RPCID, peerUniversalLink: String, reason: Reason, envelopeType: Envelope.EnvelopeType) async throws -> String { + logger.debug("Will respond with error, peerUniversalLink: \(peerUniversalLink)") let error = JSONRPCError(code: reason.code, message: reason.message) let response = RPCResponse(id: requestId, error: error) return try await respond(topic: topic, response: response, peerUniversalLink: peerUniversalLink, envelopeType: envelopeType) @@ -159,19 +199,25 @@ final class LinkEnvelopesDispatcher { private func handleRequest(topic: String, request: RPCRequest) { do { + logger.debug("handling link mode request") try rpcHistory.set(request, forTopic: topic, emmitedBy: .remote, transportType: .linkMode) requestPublisherSubject.send((topic, request)) } catch { - logger.debug(error) + logger.debug("Handling link mode request failed: \(error)") } } private func handleResponse(topic: String, response: RPCResponse) { do { + logger.debug("handling link mode response") let record = try rpcHistory.resolve(response) responsePublisherSubject.send((topic, record.request, response)) } catch { - logger.debug("Handle json rpc response error: \(error)") + logger.debug("Handling link mode response failed: \(error)") } } + + func isRunningTests() -> Bool { + return ProcessInfo.processInfo.arguments.contains("isTesting") + } } diff --git a/Sources/WalletConnectSign/Auth/Types/Errors/AuthError.swift b/Sources/WalletConnectSign/Auth/Types/Errors/AuthError.swift index 715e0cd77..7120bcf39 100644 --- a/Sources/WalletConnectSign/Auth/Types/Errors/AuthError.swift +++ b/Sources/WalletConnectSign/Auth/Types/Errors/AuthError.swift @@ -9,6 +9,7 @@ public enum AuthError: Codable, Equatable, Error, LocalizedError { case malformedRequestParams case messageCompromised case signatureVerificationFailed + case userRejectedRequest } extension AuthError: Reason { @@ -27,6 +28,8 @@ extension AuthError: Reason { self = .messageCompromised case Self.signatureVerificationFailed.code: self = .signatureVerificationFailed + case Self.userRejectedRequest.code: + self = .userRejectedRequest default: return nil } @@ -41,13 +44,15 @@ extension AuthError: Reason { case .userRejeted: return 14001 case .malformedResponseParams: - return 12001 + return 11001 case .malformedRequestParams: - return 12002 + return 11002 case .messageCompromised: - return 12003 + return 11003 case .signatureVerificationFailed: - return 12004 + return 11004 + case .userRejectedRequest: + return 12001 } } @@ -67,6 +72,8 @@ extension AuthError: Reason { return "Message verification failed" case .userDisconnected: return "User Disconnected" + case .userRejectedRequest: + return "User Rejected Request" } } diff --git a/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionResponder.swift b/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionResponder.swift index 03c43c3b9..5d215be7a 100644 --- a/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionResponder.swift +++ b/Sources/WalletConnectSign/LinkAndRelayDispatchers/LinkSessionResponder.swift @@ -26,15 +26,21 @@ class LinkSessionResponder { } func respondSessionRequest(topic: String, requestId: RPCID, response: RPCResult) async throws -> String { + logger.debug("LinkSessionResponder: responding session request") guard let session = sessionStore.getSession(forTopic: topic) else { - throw WalletConnectError.noSessionMatchingTopic(topic) + let error = WalletConnectError.noSessionMatchingTopic(topic) + logger.debug("failed: \(error)") + throw error } guard let peerUniversalLink = session.peerParticipant.metadata.redirect!.universal else { - throw Errors.missingPeerUniversalLink + let error = Errors.missingPeerUniversalLink + logger.debug("failed: \(error)") + throw error } guard sessionRequestNotExpired(requestId: requestId) else { + logger.debug("request expired") try await linkEnvelopesDispatcher.respondError( topic: topic, requestId: requestId, @@ -45,6 +51,7 @@ class LinkSessionResponder { throw Errors.sessionRequestExpired } + logger.debug("will call linkEnvelopesDispatcher.respond()") let responseEnvelope = try await linkEnvelopesDispatcher.respond( topic: topic, response: RPCResponse(id: requestId, outcome: response), @@ -55,6 +62,7 @@ class LinkSessionResponder { guard let self = self else {return} sessionRequestsProvider.emitRequestIfPending() } + logger.debug("will return response envelope: \(responseEnvelope)") return responseEnvelope } diff --git a/Sources/WalletConnectSign/LinkAndRelayDispatchers/SessionResponderDispatcher.swift b/Sources/WalletConnectSign/LinkAndRelayDispatchers/SessionResponderDispatcher.swift index 66f6d1572..fa61d3971 100644 --- a/Sources/WalletConnectSign/LinkAndRelayDispatchers/SessionResponderDispatcher.swift +++ b/Sources/WalletConnectSign/LinkAndRelayDispatchers/SessionResponderDispatcher.swift @@ -33,6 +33,7 @@ class SessionResponderDispatcher { try await relaySessionResponder.respondSessionRequest(topic: topic, requestId: requestId, response: response) return nil case .linkMode: + logger.debug("will call linkSessionResponder.respondSessionRequest()") return try await linkSessionResponder.respondSessionRequest(topic: topic, requestId: requestId, response: response) } }