集成 WalletConnectSwiftV2 按照官方文档就可以(github.com/WalletConne... WalletConnect Inc 现已更名为 Reown。作为此次过渡的一部分,我们将弃用我们支持的平台上的大量存储库/软件包,并过渡到 Reown 组织下发布的等效版本。 此存储库现已被视为已弃用,并将于 2025 年 2 月 17 日终止使用。有关更多详细信息(包括迁移指南),请参阅:docs.reown.com/advanced/wa...),
WalletConnectSwiftV2(reown-swift)是用swift实现的,所有项目用swift会方便很多 git库 github.com/WalletConne...改为github.com/reown-com/r...
pod WalletConnectSwiftV2
但有些依赖库会缺少, 需要单独pod 如:
pod 'WalletConnectSwiftV2/Web3Wallet', '1.6.6' pod 'WalletConnectSwiftV2/WalletConnectRouter', '1.6.6' pod 'WalletConnectSwiftV2/WalletConnectIdentity', '1.6.6'
dapp使用
创建DappConnect.swift 实现回调方法
swift
@objc protocol DappConnectDelegate {
@objc func dappWalletList(_ succeed:Bool,_ type:Int)
@objc func dappSelectConnect()
@objc func failedToConnect()
@objc func didConnect(_ dict:NSDictionary)
@objc func didDisconnect()
@objc func didUpdate(_ dict:NSDictionary)
}
swift
@objc public func start() {
var isDapp = true
//创建AppMetadata对象
let metadata = AppMetadata(
name: ExName,
description: ExName,
url: String(format: "https://www.%@/favicon.ico", DomainSwithTool.currentDomain()),
icons: [String(format: "https://www.%@/favicon.ico", DomainSwithTool.currentDomain())]
)
Pair.configure(metadata: metadata)
#if DEBUG
if CommandLine.arguments.contains("-cleanInstall") {
try? Sign.instance.cleanup()
}
#endif
//已断链回调
Sign.instance.sessionDeletePublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] (topic, reason) in
//self.delegate.didDisconnect()
}.store(in: &publishers)
//返回数据回调
Sign.instance.sessionResponsePublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] response in
presentResponse(for: response)
}.store(in: &publishers)
//地址(address)/链(chainId)变更回调
Sign.instance.sessionUpdatePublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] (topic, namespaces) in
onSessionUpdateNamespaces(topic, namespaces)
}.store(in: &publishers)
//连接成功返回支账户信息(链,方法名称,地址0x)
Sign.instance.sessionSettlePublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] session in
if isDapp{
isDapp = false
showAccountsScreen(session,false)
}
}.store(in: &publishers)
}
获取所有支持钱包列表
swift
func getListings() async throws -> [Listing] {
let httpClient = HTTPNetworkClient(host: "explorer-api.walletconnect.com")
let response = try await httpClient.request(
ListingsResponse.self,
at: ExplorerAPI.getListings(
projectId: Web3Modal.config.projectId,
metadata: Web3Modal.config.metadata
)
)
return response.listings.values.compactMap { $0 }
}
获取某个钱包logo url
swift
private func imageURL(for wallet: Listing?) -> URL? {
guard let wallet else { return nil }
// case small = "sm" case medium = "md" case large = "lg"
let urlString = "https://explorer-api.walletconnect.com/v3/logo/\("md")/\(wallet.imageId)?projectId=\(InputConfig.projectId)"
return URL(string: urlString)
}
Listing 类型转 WalletModel类型
swift
self.walletList.removeAll()
for wallet:Listing in self.wallets!{
let model:WalletModel = WalletModel()
model.id = wallet.id
model.name = wallet.name
model.homepage = wallet.homepage
model.order = wallet.order ?? 0
model.imageId = wallet.imageId
model.app.ios = wallet.app.ios ?? ""
model.app.mac = wallet.app.mac ?? ""
model.app.safari = wallet.app.safari ?? ""
model.mobile.native = wallet.mobile.native ?? ""
model.mobile.universal = wallet.mobile.universal ?? ""
model.imageUrl = imageURL(for: wallet)!
self.walletList .append(model)
}
连接钱包
swift
@objc func connect() {
let chain = String(format: "eip155:%@",WalletNodeManager.shared().currentNode.chainID)
let namespaces: [String: ProposalNamespace] = [
"eip155": ProposalNamespace(
chains: [
Blockchain(chain)!
],
methods: [
"eth_sendTransaction",
"personal_sign",
"eth_signTypedData"
], events: []
)
]
let optionalNamespaces: [String: ProposalNamespace] = [:
]
let sessionProperties: [String: String] = [
"caip154-mandatory": "true"
]
Task {
Web3Modal.set(sessionParams: .init(
requiredNamespaces: namespaces,
optionalNamespaces: optionalNamespaces,
sessionProperties: sessionProperties
))
let uri = try await Web3Modal.instance.connect(topic: nil)
self.uri = uri
self.uriStr = uri!.absoluteString
self.deeplinkUri = "\(uri!.deeplinkUri)"
delegate.dappSelectConnect()
}
}
唤醒钱包App
swift
@objc public func onListingTap(_ wallet: WalletModel) {
navigateToDeepLink(
universalLink: wallet.mobile.universal,
nativeLink: wallet.mobile.native
)
}
func navigateToDeepLink(universalLink: String, nativeLink: String) {
do {
let nativeUrlString = formatNativeUrlString(nativeLink)
let universalUrlString = formatUniversalUrlString(universalLink)
if let nativeUrl = nativeUrlString?.toURL() {
DispatchQueue.main.async {
UIApplication.shared.open(nativeUrl, options: [:]) { [weak self] _ in
}
}
} else if let universalUrl = universalUrlString?.toURL() {
DispatchQueue.main.async {
UIApplication.shared.open(universalUrl, options: [:]) { [weak self] _ in
}
}
} else {
//throw
}
} catch {
// toast = Toast(style: .error, message: error.localizedDescription)
}
}
func isInstall(universalLink: String, nativeLink: String) -> Bool {
do {
let nativeUrlString = formatNativeUrlString(nativeLink)
let universalUrlString = formatUniversalUrlString(universalLink)
if let nativeUrl = nativeUrlString?.toURL() {
return UIApplication.shared.canOpenURL(nativeUrl)
} else if let universalUrl = universalUrlString?.toURL() {
return UIApplication.shared.canOpenURL(universalUrl)
} else {
return false
}
} catch {
return false
}
}
func isHttpUrl(url: String) -> Bool {
return url.hasPrefix("http://") || url.hasPrefix("https://")
}
func formatNativeUrlString(_ string: String) -> String? {
if string.isEmpty { return nil }
if isHttpUrl(url: string) {
return formatUniversalUrlString(string)
}
var safeAppUrl = string
if !safeAppUrl.contains("://") {
safeAppUrl = safeAppUrl.replacingOccurrences(of: "/", with: "").replacingOccurrences(of: ":", with: "")
safeAppUrl = "\(safeAppUrl)://"
}
guard self.deeplinkUri.length > 0 else { return nil }
return "\(safeAppUrl)wc?uri=\(deeplinkUri)"
}
func formatUniversalUrlString(_ string: String) -> String? {
if string.isEmpty { return nil }
if !isHttpUrl(url: string) {
return formatNativeUrlString(string)
}
var plainAppUrl = string
if plainAppUrl.hasSuffix("/") {
plainAppUrl = String(plainAppUrl.dropLast())
}
guard self.deeplinkUri.length > 0 else { return nil }
return "\(plainAppUrl)/wc?uri=\(deeplinkUri)"
}
发起签名授权
swift
@objc func personal_sign(sign:String ,complete:@escaping (_ signHex: String)->()) {
self.completePersonal_signBlock = complete
guard let session = self.session,let chainId = self.chainId else {
return
}
let requestParams = AnyCodable([sign, self.account])
let request = Request(topic: session.topic, method: "personal_sign", params: requestParams, chainId: Blockchain(chainId)!)
Task {
do {
try await Sign.instance.request(params: request)
DispatchQueue.main.async { [weak self] in
}
} catch {
print(error)
// show failure alert
}
}
}
发起交易授权
swift
@objc func eth_sendTransaction(dict:[String:String],complete:@escaping (_ dict: NSDictionary)->()){
self.completeEth_sendTransactionBlock = complete
guard let session = self.session,let chainId = self.chainId else {
return
}
let tx = [TransactionDapp(from:self.account!,
to: dict["to"]!,
data: dict["data"]!,
chainId: dict["chainId"]!,
gas: dict["gas"]!,
gasPrice: dict["gasPrice"]!,
value: dict["value"]!,
nonce: dict["nonce"]!
)]
let requestParams = AnyCodable(tx)
let request = Request(topic: session.topic, method: "eth_sendTransaction", params: requestParams, chainId: Blockchain(chainId)!)
Task {
do {
try await Sign.instance.request(params: request)
DispatchQueue.main.async { [weak self] in
}
} catch {
print(error)
// show failure alert
}
}
}
钱包三方登录流程