Skip to main content

Mobile Linking

Note

This feature is only relevant to native platforms.

Usage

Mobile Linking allows your wallet to automatically redirect back to the Dapp allowing for less user interactions and hence a better UX for your users.

Establishing Communication Between Mobile Wallets and Apps

When integrating a wallet with a mobile application, it's essential to understand how they communicate. The process involves two main steps:

  1. QR Code Handshake: The mobile app (Dapp) generates a unique URI (Uniform Resource Identifier) and displays it as a QR code. This URI acts like a secret handshake. When the user scans the QR code using their wallet app, they establish a connection. It's like saying, "Hey, let's chat!"
  2. Deep Links and Universal Links: The URI from the QR code allows the wallet app to create a deep link or universal link. These links work on both Android and iOS. They enable seamless communication between the wallet and the app.
tip

Developers should prefer Deep Linking over Universal Linking.

Universal Linking may redirect the user to a browser, which might not provide the intended user experience. Deep Linking ensures the user is taken directly to the app.

To avoid this behavior, wallets should:

  • Restrict Redirect Metadata to Deep Link Use Cases: Redirect metadata should only be used when the session proposal is initiated through a deep link. QR code scans should not trigger app redirects using session proposal metadata.

To avoid this behavior, wallets should:

Restrict Redirect Metadata to Deep Link Use Cases: Redirect metadata should only be used when the session proposal is initiated through a deep link. QR code scans should not trigger app redirects using session proposal metadata. Ensure Unique Redirect URIs for Cross-Platform Apps: Cross-platform Dapps should use distinct redirect URIs for their mobile and desktop versions to avoid conflicts.

The connection and sign request flows are similar across platforms.

Connection Flow

  • Dapp Prompts User: The Dapp asks the user to connect.
  • User Chooses Wallet: The user selects a wallet from a list of compatible wallets.
  • Redirect to Wallet: The user is redirected to their chosen wallet.
  • Wallet Approval: The wallet prompts the user to approve or reject the session (similar to granting permission).
  • Return to Dapp:
    • Manual Return: The wallet asks the user to manually return to the Dapp.
    • Automatic Return: Alternatively, the wallet automatically takes the user back to the Dapp.
  • User Reunites with Dapp: After all the interactions, the user ends up back in the Dapp.
Mobile Linking Connect FlowMobile Linking Connect Flow

Sign Request Flow

When the Dapp needs the user to sign something (like a transaction), a similar pattern occurs:

  • Automatic Redirect: The Dapp automatically sends the user to their previously chosen wallet.
  • Approval Prompt: The wallet asks the user to approve or reject the request.
  • Return to Dapp:
    • Manual Return: The wallet asks the user to manually return to the Dapp.
    • Automatic Return: Alternatively, the wallet automatically takes the user back to the Dapp.
  • User Reconnects: Eventually, the user returns to the Dapp.
Mobile Linking Sign FlowMobile Linking Sign Flow

Platform preparations

In order for Dapps to be able to trigger your wallet for a connection or sign request using deep links you first need to add your custom scheme under CFBundleURLTypes key in your Info.plist file.

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleURLSchemes</key>
<array>
<string>examplewallet</string> <!-- your custom scheme goes here -->
</array>
</dict>
</array>
tip

Dapps developers must do the same for their own custom schemes if they want the wallet to be able to navigate back after a session approval or a sign request response

How to test

Before submitting your project to the Cloud Explorer you can test mobile linking in our sample Dapp:

  1. On your mobile device, visit the appropriate link:
  1. Click the "Custom Wallet" button and fill in the form with your wallet information. The website will reload and your wallet will be stored locally.
  2. Click the "Connect Wallet" button and choose your mobile wallet. It should automatically open and redirect to your wallet.

Learn more about mobile linking in the Best Practices section.

Integration

iOS Wallet Support

iOS has some more caveats to the integration but we ensure to make it as straightforward as possible. Since its operating system is not designed to handle multiple applications subscribing to the same deep linking schema, we've designed the AppKit to list supporting wallets on our WalletGuide and target specific deep links or universal links for each wallet.

To add your own wallet to the Explorer, login to your Reown Cloud account.

# For deep links
examplewallet://wc?uri=wc:94caa59c77dae0dd234b5818fb7292540d017b27d41f7f387ee75b22b9738c94@2?relay-protocol=irn&symKey=ce3a2c7724c03cf1769ba8b1bdedad5414cc7b920aa3fb72112b997d1916266f

# For universal links
https://example.wallet/wc?uri=wc:94caa59c77dae0dd234b5818fb7292540d017b27d41f7f387ee75b22b9738c94@2?relay-protocol=irn&symKey=ce3a2c7724c03cf1769ba8b1bdedad5414cc7b920aa3fb72112b997d1916266f

Additionally when there is a signing request triggered by the Dapp it will hit the deep link with an incomplete URI, this should be ignored and not considered valid as it's only used for automatically redirecting the users to approve or reject a signing request.

# For deep links
examplewallet://wc?uri=wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@2

# For universal links
https://example.wallet/wc?uri=wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@2

WalletConnectRouter

Overview

WalletConnectRouter simplifies navigation by automatically redirecting users back to the DApp after they've interacted with a wallet via a deep link. This eliminates the need for users to manually navigate back after approving a session or confirming a transaction.

Key Features

Automatic Redirection: By invoking WalletConnectRouter.goBack(uri: "example://")—where "example://" is the DApp's custom scheme as declared in their AppMetadata redirect field—users are seamlessly returned to the DApp.

Important Consideration

Mandatory redirect Field: Starting with WalletConnect SDK version 1.9.5, specifying the redirect field in the AppMetadata object is mandatory to avoid redirection issues.

Installation and Usage

import WalletConnectRouter

try await Sign.instance.approve(proposalId: <proposalId>, namespaces: <namespaces>)

if let uri = proposal.proposer.redirect?.native {
WalletConnectRouter.goBack(uri: uri)
} else {
// Inform the user to manually return to the DApp
}

Limitations

This section outlines some of the known limitations and constraints when using WalletConnect on iOS.

Redirects on iOS 17 and Above

Automatic redirection to browser-based DApps after wallet interaction is not possible from iOS 17 onwards. Developers should adjust their app's UI to inform users about manual navigation back to the browser.

For iOS versions below 17, WalletConnectRouter.goBack(uri: uri) facilitates automatic redirection.

Redirect

caution

Developers should prefer Deep Linking over Universal Linking.

In the case of Universal Linking, the user may be redirected to the browser, which may not be the desired behavior. Deep Linking ensures that the user is redirected to the app, providing a seamless experience.

When using WalletConnect on iOS and triggering a wallet interaction (e.g. when sending a transaction or signing a message), you may experience issues where the native app is not opened as expected and a browser navigation occurs instead.

This issue occurs because Universal Links (app links) on iOS will only open the native app when the following rules are followed:

  • The wallet interaction must be triggered by a user-initiated event, e.g. in a click handler rather than on page load or in an asynchronous callback.
  • The wallet interaction must be triggered as soon as possible within the event handler. Any preceding asynchronous work (e.g. estimating gas, resolving an ENS name, fetching a nonce) should have already completed before the event handler fires. This may require you to design the user experience around this constraint, preventing users from initiating a wallet interaction until it's ready rather than doing the work lazily.

Note that even if your own code follows these rules, libraries you depend on may be running their own asynchronous logic before triggering a wallet interaction. For example, Ethers asynchronously populates transactions before sending them. Known workarounds are documented below, but if you're still experiencing these issues, you should raise them with the relevant library maintainers.

For Ethers v5 (Legacy)

These are the known workarounds for avoiding app linking issues on iOS when using Ethers v5.

When sending a transaction

  1. signer.sendTransaction should be avoided in favor of signer.sendUncheckedTransaction
     This avoids an asynchronous call to retrieve the internal block number which Ethers uses to resolve a complete TransactionResponse object.
     Note that as a result of this optimization, sendUncheckedTransaction returns a mock transaction response that only contains the hash property and a wait method. All other properties are null.
  2. The transaction's to property should be a plain address rather than an ENS name
    This avoids an asynchronous call to automatically resolve ENS names during the send process.
     If you still want to support ENS name resolution, you should manually run provider.resolveName ahead of time, storing the result before the user attempts to send a transaction. Do not resolve ENS names in the event handler.
  3. The transaction's gasLimit property should be set
    This avoids the asynchronous work performed in sendTransaction which automatically estimates the gas limit if it's missing.
     If you still want to use the same gas limit estimation logic from sendTransaction, you should manually run provider.estimateGas ahead of time, storing the result before the user attempts to send the transaction. Do not estimate gas in the event handler.

When calling a write method on a contract

  1. contract.METHOD_NAME should be avoided if favor of calling contract.populateTransaction.METHOD_NAME ahead of time, then sending the populated transaction with signer.sendUncheckedTransaction.

  2. When sending the populated transaction, you should follow the same guidelines as regular transactions to avoid any asynchronous logic breaking the app link navigation. Do not populate the contract transaction in the event handler.

When signing a message

If the message depends on the result of an asynchronous call (e.g. retrieving a nonce when implementing Sign-In With Ethereum), you should do this work ahead of time, storing the result before the user attempts to sign the message. Do not perform this asynchronous work in the event handler.