diff --git a/Sources/Web3Modal/Core/MagicMessage.swift b/Sources/Web3Modal/Core/MagicMessage.swift deleted file mode 100644 index 5a7030c..0000000 --- a/Sources/Web3Modal/Core/MagicMessage.swift +++ /dev/null @@ -1,275 +0,0 @@ -import Foundation - - -protocol MagicMessageType: Codable { - var rawValue: String { get } -} - -enum MagicMessageResponseType: String, MagicMessageType, Codable { - case syncThemeSuccess = "@w3m-frame/SYNC_THEME_SUCCESS" - case syncDataSuccess = "@w3m-frame/SYNC_DAPP_DATA_SUCCESS" - case connectEmailSuccess = "@w3m-frame/CONNECT_EMAIL_SUCCESS" - case connectEmailError = "@w3m-frame/CONNECT_EMAIL_ERROR" - case isConnectSuccess = "@w3m-frame/IS_CONNECTED_SUCCESS" - case isConnectError = "@w3m-frame/IS_CONNECTED_ERROR" - case connectOtpSuccess = "@w3m-frame/CONNECT_OTP_SUCCESS" - case connectOtpError = "@w3m-frame/CONNECT_OTP_ERROR" - case getUserSuccess = "@w3m-frame/GET_USER_SUCCESS" - case getUserError = "@w3m-frame/GET_USER_ERROR" - case sessionUpdate = "@w3m-frame/SESSION_UPDATE" - case switchNetworkSuccess = "@w3m-frame/SWITCH_NETWORK_SUCCESS" - case switchNetworkError = "@w3m-frame/SWITCH_NETWORK_ERROR" - case rpcRequestSuccess = "@w3m-frame/RPC_REQUEST_SUCCESS" - case rpcRequestError = "@w3m-frame/RPC_REQUEST_ERROR" -} - -enum MagicMessageRequestType: String, MagicMessageType, Codable { - case syncTheme = "@w3m-app/SYNC_THEME" - case syncData = "@w3m-app/SYNC_DAPP_DATA" - case connectEmail = "@w3m-app/CONNECT_EMAIL" - case connectDevice = "@w3m-app/CONNECT_DEVICE" - case isConnected = "@w3m-app/IS_CONNECTED" - case connectOtp = "@w3m-app/CONNECT_OTP" - case switchNetwork = "@w3m-app/SWITCH_NETWORK" - case rpcRequest = "@w3m-app/RPC_REQUEST" - case signOut = "@w3m-app/SIGN_OUT" - case getUser = "@w3m-app/GET_USER" - case getChainId = "@w3m-app/GET_CHAIN_ID" - case updateEmail = "@w3m-app/UPDATE_EMAIL" -} - -class MagicMessage: Decodable { - var type: MagicMessageType - var payload: AnyCodable? - var rt: String? - var jwt: String? - var action: String? - - enum CodingKeys: CodingKey { - case type - case payload - case rt - case jwt - case action - } - - init(type: MagicMessageType, payload: AnyCodable? = nil, rt: String? = nil, jwt: String? = nil, action: String? = nil) { - self.type = type - self.payload = payload - self.rt = rt - self.jwt = jwt - self.action = action - } - - required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.type = try container.decode(MagicMessageResponseType.self, forKey: .type) - self.payload = try container.decodeIfPresent(AnyCodable.self, forKey: .payload) - self.rt = try container.decodeIfPresent(String.self, forKey: .rt) - self.jwt = try container.decodeIfPresent(String.self, forKey: .jwt) - self.action = try container.decodeIfPresent(String.self, forKey: .action) - } - - var toString: String { return "{type: \"\(type)\"}" } -} - -class IsConnected: MagicMessage { - init() { - super.init(type: MagicMessageRequestType.isConnected) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } -} - -class SwitchNetwork: MagicMessage { - let chainId: String - - init(chainId: String) { - self.chainId = chainId - super.init(type: MagicMessageRequestType.switchNetwork) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - let container = try decoder.container(keyedBy: CodingKeys.self) - } - - override var toString: String { - "{type:'\(type.rawValue)',payload:{chainId:\(chainId)}}" - } -} - -class ConnectEmail: MagicMessage { - let email: String - - init(email: String) { - self.email = email - super.init(type: MagicMessageRequestType.connectEmail) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - override var toString: String { - "{type:'\(type)',payload:{email:'\(email)'}}" - } -} - -class ConnectDevice: MagicMessage { - init() { - super.init(type: MagicMessageRequestType.connectDevice) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } -} - -class ConnectOtp: MagicMessage { - let otp: String - - init(otp: String) { - self.otp = otp - super.init(type: MagicMessageRequestType.connectOtp) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - override var toString: String { - "{type:'\(type)',payload:{otp:'\(otp)'}}" - } -} - -class GetUser: MagicMessage { - - let chainId: String? - - init(chainId: String?) { - self.chainId = chainId - super.init(type: MagicMessageRequestType.getUser) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - override var toString: String { - if let chainId { - return "{type:'\(type)',payload:{chainId:\(chainId)}}" - } else { - return "{type:'\(type)'}" - } - } -} - -class SignOut: MagicMessage { - init() { - super.init(type: MagicMessageRequestType.signOut) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } -} - -class GetChainId: MagicMessage { - init() { - super.init(type: MagicMessageRequestType.getChainId) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } -} - -class RpcRequest: MagicMessage { - let method: String - let params: [Any] - - init(method: String, params: [Any]) { - self.method = method - self.params = params - super.init(type: MagicMessageRequestType.rpcRequest) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - override var toString: String { - let m = "method:'\(method)'" - let p = params.map { "'\($0)'" }.joined(separator: ",") - return "{type:'\(type)',payload:{\(m),params:[\(p)]}}" - } -} - -// readonly APP_AWAIT_UPDATE_EMAIL: "@w3m-app/AWAIT_UPDATE_EMAIL"; -class UpdateEmail: MagicMessage { - init() { - super.init(type: MagicMessageRequestType.updateEmail) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } -} - -class SyncTheme: MagicMessage { - var mode: String - - init(mode: String = "light") { - self.mode = mode - super.init(type: MagicMessageRequestType.syncTheme) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - override var toString: String { - let tm = "themeMode:'\(mode)'" - return "{type:'\(type)',payload:{\(tm)}}" - } -} - -struct PairingMetadata { - let name: String - let description: String - let url: String - let icons: [String] -} - -class SyncAppData: MagicMessage { - let metadata: PairingMetadata - let sdkVersion: String - let projectId: String - - init(metadata: PairingMetadata, sdkVersion: String, projectId: String) { - self.metadata = metadata - self.sdkVersion = sdkVersion - self.projectId = projectId - super.init(type: MagicMessageRequestType.syncData) - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - override var toString: String { - let v = "verified: true" - let p1 = "projectId:'\(projectId)'" - let p2 = "sdkVersion:'\(sdkVersion)'" - let m1 = "name:'\(metadata.name)'" - let m2 = "description:'\(metadata.description)'" - let m3 = "url:'\(metadata.url)'" - let m4 = "icons:[\"\(metadata.icons.first ?? "")\"]" - let p3 = "metadata:{\(m1),\(m2),\(m3),\(m4)}" - let p = "payload:{\(v),\(p1),\(p2),\(p3)}" - return "{type:'\(type)',\(p)}" - } -} diff --git a/Sources/Web3Modal/Core/MagicRequest.swift b/Sources/Web3Modal/Core/MagicRequest.swift new file mode 100644 index 0000000..73fc066 --- /dev/null +++ b/Sources/Web3Modal/Core/MagicRequest.swift @@ -0,0 +1,27 @@ +import Foundation + +struct MagicResponse: Decodable { + enum MessageType: String, Decodable { + case syncThemeSuccess = "@w3m-frame/SYNC_THEME_SUCCESS" + case syncDataSuccess = "@w3m-frame/SYNC_DAPP_DATA_SUCCESS" + case connectEmailSuccess = "@w3m-frame/CONNECT_EMAIL_SUCCESS" + case connectEmailError = "@w3m-frame/CONNECT_EMAIL_ERROR" + case isConnectSuccess = "@w3m-frame/IS_CONNECTED_SUCCESS" + case isConnectError = "@w3m-frame/IS_CONNECTED_ERROR" + case connectOtpSuccess = "@w3m-frame/CONNECT_OTP_SUCCESS" + case connectOtpError = "@w3m-frame/CONNECT_OTP_ERROR" + case getUserSuccess = "@w3m-frame/GET_USER_SUCCESS" + case getUserError = "@w3m-frame/GET_USER_ERROR" + case sessionUpdate = "@w3m-frame/SESSION_UPDATE" + case switchNetworkSuccess = "@w3m-frame/SWITCH_NETWORK_SUCCESS" + case switchNetworkError = "@w3m-frame/SWITCH_NETWORK_ERROR" + case rpcRequestSuccess = "@w3m-frame/RPC_REQUEST_SUCCESS" + case rpcRequestError = "@w3m-frame/RPC_REQUEST_ERROR" + } + + var type: MessageType + var payload: AnyCodable? + var rt: String? + var jwt: String? + var action: String? +} diff --git a/Sources/Web3Modal/Core/MagicResponse.swift b/Sources/Web3Modal/Core/MagicResponse.swift new file mode 100644 index 0000000..9abf3aa --- /dev/null +++ b/Sources/Web3Modal/Core/MagicResponse.swift @@ -0,0 +1,193 @@ +import Foundation +import WalletConnectSign + +protocol MagicMessageRequest { + var type: MagicRequest.MessageType { get } + var toString: String { get } +} + +extension MagicMessageRequest { + var toString: String { + return #" { "type": "\#(type)"} "# + } +} + +enum MagicRequest { + + enum MessageType: String { + case syncTheme = "@w3m-app/SYNC_THEME" + case syncData = "@w3m-app/SYNC_DAPP_DATA" + case connectEmail = "@w3m-app/CONNECT_EMAIL" + case connectDevice = "@w3m-app/CONNECT_DEVICE" + case isConnected = "@w3m-app/IS_CONNECTED" + case connectOtp = "@w3m-app/CONNECT_OTP" + case switchNetwork = "@w3m-app/SWITCH_NETWORK" + case rpcRequest = "@w3m-app/RPC_REQUEST" + case signOut = "@w3m-app/SIGN_OUT" + case getUser = "@w3m-app/GET_USER" + case getChainId = "@w3m-app/GET_CHAIN_ID" + case updateEmail = "@w3m-app/UPDATE_EMAIL" + } + + struct IsConnected: MagicMessageRequest { + let type: MessageType + + init() { + self.type = MessageType.isConnected + } + } + + struct SwitchNetwork: MagicMessageRequest { + let chainId: String + let type: MessageType + + init(chainId: String) { + self.chainId = chainId + self.type = MessageType.switchNetwork + } + + var toString: String { + "{type:'\(type.rawValue)',payload:{chainId:\(chainId)}}" + } + } + + struct ConnectEmail: MagicMessageRequest { + let type: MessageType + let email: String + + init(email: String) { + self.email = email + self.type = MessageType.connectEmail + } + + var toString: String { + "{type:'\(type)',payload:{email:'\(email)'}}" + } + } + + struct ConnectDevice: MagicMessageRequest { + let type: MessageType + + init() { + self.type = MessageType.connectDevice + } + } + + struct ConnectOtp: MagicMessageRequest { + let otp: String + let type: MessageType + + init(otp: String) { + self.otp = otp + self.type = MessageType.connectOtp + } + + var toString: String { + "{type:'\(type)',payload:{otp:'\(otp)'}}" + } + } + + struct GetUser: MagicMessageRequest { + let type: MessageType + let chainId: String? + + init(chainId: String?) { + self.chainId = chainId + self.type = MessageType.getUser + } + + var toString: String { + if let chainId { + return "{type:'\(type)',payload:{chainId:\(chainId)}}" + } else { + return "{type:'\(type)'}" + } + } + } + + struct SignOut: MagicMessageRequest { + let type: MessageType + + init() { + self.type = MessageType.signOut + } + } + + struct GetChainId: MagicMessageRequest { + let type: MessageType + + init() { + self.type = MessageType.getChainId + } + } + + struct RpcRequest: MagicMessageRequest { + let method: String + let params: AnyCodable + let type: MessageType + + init(method: String, params: AnyCodable) { + self.method = method + self.params = params + self.type = MessageType.rpcRequest + } + + // TODO: Properly convert params to string + var toString: String { + let m = "method:'\(method)'" + let p = "" // params.map { "'\($0)'" }.joined(separator: ",") + return "{type:'\(type)',payload:{\(m),params:[\(p)]}}" + } + } + + // readonly APP_AWAIT_UPDATE_EMAIL: "@w3m-app/AWAIT_UPDATE_EMAIL"; + struct UpdateEmail: MagicMessageRequest { + let type: MessageType + + init() { + self.type = MessageType.updateEmail + } + } + + struct SyncTheme: MagicMessageRequest { + var mode: String + let type: MessageType + + init(mode: String = "light") { + self.mode = mode + self.type = MessageType.syncTheme + } + + var toString: String { + let tm = "themeMode:'\(mode)'" + return "{type:'\(type)',payload:{\(tm)}}" + } + } + + struct SyncAppData: MagicMessageRequest { + let metadata: AppMetadata + let sdkVersion: String + let projectId: String + let type: MessageType + + init(metadata: AppMetadata, sdkVersion: String, projectId: String) { + self.metadata = metadata + self.sdkVersion = sdkVersion + self.projectId = projectId + self.type = MessageType.syncData + } + + var toString: String { + let v = "verified: true" + let p1 = "projectId:'\(projectId)'" + let p2 = "sdkVersion:'\(sdkVersion)'" + let m1 = "name:'\(metadata.name)'" + let m2 = "description:'\(metadata.description)'" + let m3 = "url:'\(metadata.url)'" + let m4 = "icons:[\"\(metadata.icons.first ?? "")\"]" + let p3 = "metadata:{\(m1),\(m2),\(m3),\(m4)}" + let p = "payload:{\(v),\(p1),\(p2),\(p3)}" + return "{type:'\(type)',\(p)}" + } + } +} diff --git a/Sources/Web3Modal/Core/MagicService.swift b/Sources/Web3Modal/Core/MagicService.swift index 2c63266..7673915 100644 --- a/Sources/Web3Modal/Core/MagicService.swift +++ b/Sources/Web3Modal/Core/MagicService.swift @@ -19,7 +19,7 @@ class MagicService { private let url = "https://secure.walletconnect.com" private let projectId: String = Web3Modal.config.projectId -// private let metadata: PairingMetadata + private let metadata: AppMetadata = Web3Modal.config.metadata private let messageHandler: MessageHandler private let navigationDelegate: NavigationDelegate @@ -80,29 +80,29 @@ class MagicService { } func connectEmail(email: String) async { - let message = ConnectEmail(email: email).toString + let message = MagicRequest.ConnectEmail(email: email).toString await runJavaScript("sendMessage(\(message))") } func connectDevice() async { - let message = ConnectDevice().toString + let message = MagicRequest.ConnectDevice().toString await runJavaScript("sendMessage(\(message))") } func connectOtp(otp: String) async { // Assuming waitConfirmation is a property you're using to track state // waitConfirmation.value = true - let message = ConnectOtp(otp: otp).toString + let message = MagicRequest.ConnectOtp(otp: otp).toString await runJavaScript("sendMessage(\(message))") } func isConnected() async { - let message = IsConnected().toString + let message = MagicRequest.IsConnected().toString await runJavaScript("sendMessage(\(message))") } func getChainId() async { - let message = GetChainId().toString + let message = MagicRequest.GetChainId().toString await runJavaScript("sendMessage(\(message))") } @@ -113,22 +113,26 @@ class MagicService { func syncTheme(theme: Web3ModalTheme?) async { guard let mode = theme?.rawValue else { return } - let message = SyncTheme(mode: mode) + let message = MagicRequest.SyncTheme(mode: mode) await runJavaScript("sendMessage(\(message))") } - func syncDappData(metadata: PairingMetadata, sdkVersion: String, projectId: String) async { - let message = SyncAppData(metadata: metadata, sdkVersion: sdkVersion, projectId: projectId).toString + func syncDappData( + metadata: AppMetadata, + sdkVersion: String, + projectId: String + ) async { + let message = MagicRequest.SyncAppData(metadata: metadata, sdkVersion: sdkVersion, projectId: projectId).toString await runJavaScript("sendMessage(\(message))") } func getUser(chainId: String?) async { - let message = GetUser(chainId: chainId).toString + let message = MagicRequest.GetUser(chainId: chainId).toString await runJavaScript("sendMessage(\(message))") } func switchNetwork(chainId: String) async { - let message = SwitchNetwork(chainId: chainId).toString + let message = MagicRequest.SwitchNetwork(chainId: chainId).toString await runJavaScript("sendMessage(\(message))") } @@ -169,12 +173,51 @@ class MessageHandler: NSObject, WKScriptMessageHandler { let data = bodyString.data(using: .utf8) else { return } do { - let jsonMessage = try JSONDecoder().decode(MagicMessage.self, from: data) - // Handle the decoded message + let response = try JSONDecoder().decode(MagicResponse.self, from: data) + handleMagicResponse(response) } catch { print("Error decoding message: \(error)") } } + + func handleMagicResponse(_ response: MagicResponse) { + + print(response) + + switch response.type { + case .syncThemeSuccess: + break + case .syncDataSuccess: + break + case .connectEmailSuccess: + break + case .connectEmailError: + break + case .isConnectSuccess: + break + case .isConnectError: + break + case .connectOtpSuccess: + break + case .connectOtpError: + break + case .getUserSuccess: + break + case .getUserError: + break + case .sessionUpdate: + break + case .switchNetworkSuccess: + break + case .switchNetworkError: + break + case .rpcRequestSuccess: + break + case .rpcRequestError: + break + } + } + } class NavigationDelegate: NSObject, WKNavigationDelegate {