Skip to content

Commit

Permalink
Merge pull request #150 from MetaMask/rn-sdk-support
Browse files Browse the repository at this point in the history
feat: React Native SDK Integration Support
  • Loading branch information
elefantel authored May 22, 2024
2 parents 1642d33 + cba6f6e commit 5bff8f8
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 40 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Alternatively, you can add the URL directly in your project's package file:
dependencies: [
.package(
url: "https://github.com/MetaMask/metamask-ios-sdk",
from: "0.6.2"
from: "0.6.3"
)
]
```
Expand Down
223 changes: 187 additions & 36 deletions Sources/metamask-ios-sdk/Classes/Ethereum/Ethereum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ public class Ethereum {
return ethereum
}

func updateTransportLayer(_ transport: Transport) {
public var transport: Transport = .socket

@discardableResult
func updateTransportLayer(_ transport: Transport) -> Ethereum {
disconnect()
self.transport = transport

switch transport {
case .deeplinking(let dappScheme):
Expand All @@ -65,6 +69,7 @@ public class Ethereum {

commClient.trackEvent = trackEvent
commClient.handleResponse = handleMessage
return self
}

private func trackEvent(event: Event, parameters: [String: Any]) {
Expand Down Expand Up @@ -159,18 +164,70 @@ public class Ethereum {
)
connected = true

if commClient is SocketClient {
switch transport {
case .socket:
commClient.connect(with: nil)
return request(connectWithRequest)

// React Native SDK has request params as Data
if let paramsData = req.params as? Data {
let reqJson = String(data: paramsData, encoding: .utf8)?.trimEscapingChars() ?? ""
let requestItem: EthereumRequest = EthereumRequest(
id: req.id,
method: req.method,
params: reqJson
)

let connectWithParams = [requestItem]
let connectRequest = EthereumRequest(
id: connectWithRequest.id,
method: connectWithRequest.method,
params: connectWithParams
)
return request(connectRequest)
} else {
return request(connectWithRequest)
}
case .deeplinking(_):
let submittedRequest = SubmittedRequest(method: connectWithRequest.method)
submittedRequests[connectWithRequest.id] = submittedRequest
let publisher = submittedRequests[connectWithRequest.id]?.publisher

// React Native SDK has request params as Data
if let paramsData = req.params as? Data {
do {
let params = try JSONSerialization.jsonObject(with: paramsData, options: [])

let requestDict: [String: Any] = [
"id": req.id,
"method": req.method,
"params": params
]

let jsonData = try JSONSerialization.data(withJSONObject: requestDict)
let jsonParams = try JSONSerialization.jsonObject(with: jsonData, options: [])

let connectWithParams = [jsonParams]

let connectWithDict: [String: Any] = [
"id": connectWithRequest.id,
"method": connectWithRequest.method,
"params": connectWithParams
]

let connectWithData = try JSONSerialization.data(withJSONObject: connectWithDict)

let connectWithJson = String(data: connectWithData, encoding: .utf8)?.trimEscapingChars() ?? ""

commClient.connect(with: connectWithJson)
} catch {
Logging.error("Ethereum:: error: \(error.localizedDescription)")
}
} else {
let requestJson = connectWithRequest.toJsonString() ?? ""
commClient.connect(with: requestJson)
}
return publisher
}

let submittedRequest = SubmittedRequest(method: connectWithRequest.method)
submittedRequests[connectWithRequest.id] = submittedRequest
let publisher = submittedRequests[connectWithRequest.id]?.publisher
let requestJson = connectWithRequest.toJsonString() ?? ""

commClient.connect(with: requestJson)
return publisher
}

func connectWith<T: CodableData>(_ req: EthereumRequest<T>) async -> Result<String, RequestError> {
Expand Down Expand Up @@ -253,7 +310,7 @@ public class Ethereum {
await ethereumRequest(method: .ethGetTransactionCount, params: [address, tagOrblockNumber])
}

func addEthereumChain(chainId: String,
func addEthereumChain(chainId: String,
chainName: String,
rpcUrls: [String],
iconUrls: [String]?,
Expand Down Expand Up @@ -338,22 +395,68 @@ public class Ethereum {
"from": "mobile",
"method": request.method
])
if commClient is SocketClient {
(commClient as? SocketClient)?.sendMessage(request, encrypt: true)

switch transport {
case .socket:
// React Native SDK has request params as Data
if let paramsData = request.params as? Data {
do {
let params = try JSONSerialization.jsonObject(with: paramsData, options: [])

let requestDict: [String: Any] = [
"id": request.id,
"method": request.method,
"params": params
]

let requestData = try JSONSerialization.data(withJSONObject: requestDict)


let requestJson = String(data: requestData, encoding: .utf8)?.trimEscapingChars() ?? ""

commClient.sendMessage(requestJson, encrypt: true)
} catch {
Logging.error("Ethereum:: error: \(error.localizedDescription)")
}
} else {
(commClient as? SocketClient)?.sendMessage(request, encrypt: true)
}

let authorise = EthereumMethod.requiresAuthorisation(request.methodType)
let skipAuthorisation = request.methodType == .ethRequestAccounts && !account.isEmpty

if authorise && !skipAuthorisation {
(commClient as? SocketClient)?.requestAuthorisation()
}
} else {
guard let requestJson = request.toJsonString() else {
Logging.error("Ethereum:: could not convert request to JSON: \(request)")
return
}

commClient.sendMessage(requestJson, encrypt: true)
case .deeplinking(_):
// React Native SDK has request params as Data
if let paramsData = request.params as? Data {
do {
let params = try JSONSerialization.jsonObject(with: paramsData, options: [])

let requestDict: [String: Any] = [
"id": request.id,
"method": request.method,
"params": params
]

let jsonData = try JSONSerialization.data(withJSONObject: requestDict)
let requestJson = String(data: jsonData, encoding: .utf8)?.trimEscapingChars() ?? ""

commClient.sendMessage(requestJson, encrypt: true)
} catch {
Logging.error("Ethereum:: error: \(error.localizedDescription)")
return
}
} else {
guard let requestJson = request.toJsonString() else {
Logging.error("Ethereum:: could not convert request to JSON: \(request)")
return
}

commClient.sendMessage(requestJson, encrypt: true)
}
}
}
}
Expand Down Expand Up @@ -465,23 +568,71 @@ public class Ethereum {
}
}

func batchRequest<T: CodableData>(_ params: [EthereumRequest<T>]) async -> Result<[String], RequestError> {
let batchRequest = EthereumRequest(
method: EthereumMethod.metamaskBatch.rawValue,
params: params)

return await withCheckedContinuation { continuation in
request(batchRequest)?
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
continuation.resume(returning: .success([]))
case .failure(let error):
continuation.resume(returning: .failure(error))
func batchRequest<T: CodableData>(_ requests: [EthereumRequest<T>]) async -> Result<[String], RequestError> {

// React Native SDK has request params as Data
if let _ = requests.first?.params as? Data {
var requestDicts: [[String: Any]] = []

for request in requests {
if let paramData = request.params as? Data {
do {
let requestParams = try JSONSerialization.jsonObject(with: paramData, options: [])

let dict: [String: Any] = [
"id": request.id,
"method": request.method,
"params": requestParams
]
requestDicts.append(dict)
} catch {
Logging.error("Ethereum:: error: \(error.localizedDescription)")
return .failure(RequestError(from: ["message": error.localizedDescription]))
}
}, receiveValue: { result in
continuation.resume(returning: .success(result as? [String] ?? []))
}).store(in: &cancellables)
}
}

do {
let jsonData = try JSONSerialization.data(withJSONObject: requestDicts)
let batchReq = EthereumRequest(
method: EthereumMethod.metamaskBatch.rawValue,
params: jsonData)

return await withCheckedContinuation { continuation in
request(batchReq)?
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
continuation.resume(returning: .success([]))
case .failure(let error):
continuation.resume(returning: .failure(error))
}
}, receiveValue: { result in
continuation.resume(returning: .success(result as? [String] ?? []))
}).store(in: &cancellables)
}
} catch {
Logging.error("Ethereum:: error: \(error.localizedDescription)")
return .failure(RequestError(from: ["message": error.localizedDescription]))
}
} else {
let batchRequest = EthereumRequest(
method: EthereumMethod.metamaskBatch.rawValue,
params: requests)

return await withCheckedContinuation { continuation in
request(batchRequest)?
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
continuation.resume(returning: .success([]))
case .failure(let error):
continuation.resume(returning: .failure(error))
}
}, receiveValue: { result in
continuation.resume(returning: .success(result as? [String] ?? []))
}).store(in: &cancellables)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/metamask-ios-sdk/Classes/SDK/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public final class Dependencies {

return Ethereum.shared(commClient: client) { event, parameters in
self.trackEvent(event, parameters: parameters)
}
}.updateTransportLayer(transport)
}

public lazy var keyExchange: KeyExchange = KeyExchange()
Expand Down
5 changes: 4 additions & 1 deletion Sources/metamask-ios-sdk/Classes/SDK/MetaMaskSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public class MetaMaskSDK: ObservableObject {
}
}

public var transport: Transport

public var networkUrl: String {
get {
(ethereum.commClient as? SocketClient)?.networkUrl ?? ""
Expand All @@ -56,6 +58,7 @@ public class MetaMaskSDK: ObservableObject {

private init(appMetadata: AppMetadata, transport: Transport, enableDebug: Bool, sdkOptions: SDKOptions?) {
self.ethereum = Dependencies.shared.ethereum(transport: transport)
self.transport = transport
self.ethereum.delegate = self
self.ethereum.sdkOptions = sdkOptions
self.ethereum.updateMetadata(appMetadata)
Expand All @@ -67,7 +70,7 @@ public class MetaMaskSDK: ObservableObject {
(ethereum.commClient as? DeeplinkClient)?.handleUrl(url)
}

public static func shared(_ appMetadata: AppMetadata,
public static func shared(_ appMetadata: AppMetadata,
transport: Transport = .socket,
enableDebug: Bool = true,
sdkOptions: SDKOptions?) -> MetaMaskSDK {
Expand Down
2 changes: 1 addition & 1 deletion metamask-ios-sdk.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'metamask-ios-sdk'
s.version = '0.6.2'
s.version = '0.6.3'
s.summary = 'Enable users to easily connect with their MetaMask Mobile wallet.'
s.swift_version = '5.5'

Expand Down

0 comments on commit 5bff8f8

Please sign in to comment.