Mobile Linking
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:
- 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!"
- 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.
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.
Key Behavior to Address
In some scenarios, wallets use redirect metadata provided in session proposals to open applications. This can cause unintended behavior, such as:
Redirecting to the wrong app when multiple apps share the same redirect metadata (e.g., a desktop and mobile version of the same Dapp). Opening an unrelated application if a QR code is scanned on a different device than where the wallet is installed.
Recommended Approach
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.
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.
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.
Platform preparations
- iOS
- Android
- Flutter
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.
For instance, if your Wallet's name is Example Wallet, your custom scheme would be more likely as examplewallet://
, therefor you will add the following in your iOS's 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>
In order for Dapps to be able to trigger your wallet for a connection or sign request using deep links you first need to declare an <intent-filter>
in your wallet's Manifest.xml as follows:
For instance, if your Wallet's name is Example Wallet, your custom scheme would be more likely as examplewallet://
, therefor you will add the following intent filter in your Android's Manifest.xml file:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="examplewallet" /> <!-- your own custom scheme -->
</intent-filter>
Since Flutter leverages on native APIs, you must follow iOS and Android steps for each native platform.
Additionally, you would have to set FlutterDeepLinkingEnabled key to true on iOS's Info.plist file.
<key>FlutterDeepLinkingEnabled</key>
<true/>
More information in official documentation: https://docs.flutter.dev/ui/navigation/deep-linking
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:
- On your mobile device, visit the appropriate link:
- For EVM: https://appkit-lab.reown.com/library/wagmi/
- For Solana: https://appkit-lab.reown.com/library/solana/
- 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.
- 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
Either you are approving a session proposal or responding to a session request, redirecting back to the Dapp is as simply as launching requester's redirect
object in PairingMetadata
, the same way as Dapps would call your wallet's redirect
object on their side:
A dapp would call examplewallet://wc?uri={pairingUri}
from their side when they request to connect with your wallet, and given the fact that examplewallet
is your registered custom scheme then your wallet will be opened.
Redirecting back to dapp (proposer) after session approval:
WalletKit SDK exports a handy method for easy redirection back to the requester app, whether it be after a session proposal, a session authentica or a session request.
Future<bool> redirectToDapp({
required String topic,
required Redirect? redirect,
})
After Session Proposal:
_walletKit!.onSessionProposal.subscribe(_onSessionProposal);
//
void _onSessionProposal(SessionProposalEvent? event) async {
if (event != null) {
// Process session proposal
// ....
// Redirect back to proposer dapp
try {
await _walletKit.redirectToDapp(
topic: topic,
redirect: event.params.proposer.metadata.redirect,
);
} catch (e) {
...
}
}
}
After Session Authenticate:
// If your wallet supports One-Click Auth
_walletKit!.onSessionAuthRequest.subscribe(_onSessionAuthRequest);
//
void _onSessionAuthRequest(SessionAuthRequest? event) async {
if (event != null) {
// Process session authentication
// ....
// Redirect back to proposer dapp
try {
await _walletKit.redirectToDapp(
topic: topic,
redirect: event.params.proposer.metadata.redirect,
);
} catch (e) {
...
}
}
}
A dapp would call examplewallet://
(or even better session.peer?.metadata.redirect?.native
object) from their side when they request to sign a transaction, and given the fact that session.peer?.metadata.redirect?.native
contains your registered custom scheme (examplewallet://
) then your wallet will be opened.
Redirecting back to dapp (proposer) after responding to a sign request:
// Your registered request handler for the given requested method will be triggered
Future<void> personalSignRequestHandler(String topic, dynamic parameters) async {
// Process signing requests
// ...
// With the given topic with retrieve the current session data
final session = _walletKit.sessions.get(topic);
// And we get the peer metadata to trigger dapp's redirect value
try {
await _walletKit.redirectToDapp(
topic: topic,
redirect: session!.peer.metadata.redirect,
);
} catch (e) {
...
}
}
launchUrlString()
from url_launcher oficial package was used as an example to explain the mechanism, you can choose whatever other package you would like.