Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link Mode #312

Merged
merged 25 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.4.0-beta01

- Link Mode! https://docs.walletconnect.com/walletkit/flutter/link-mode

## 2.3.1

- Added Connectivity check to core and throw exceptions when internet connection is gone
Expand Down
60 changes: 48 additions & 12 deletions example/dapp/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.INTERNET" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<package android:name="com.walletconnect.flutterwallet"/>
<package android:name="com.walletconnect.flutterwallet.internal"/>

<package android:name="com.walletconnect.flutterwallet" />
<package android:name="com.walletconnect.flutterwallet.internal" />
</queries>
<application
android:label="${applicationLabel}"
android:name="${applicationName}"
android:icon="${appIcon}">
<application
android:label="${applicationLabel}"
android:name="${applicationName}"
android:icon="${appIcon}">
<activity
android:name="com.example.dapp.MainActivity"
android:exported="true"
Expand All @@ -27,25 +29,59 @@
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />

<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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="wcflutterdapp" />
</intent-filter>
<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="wcflutterdapp-internal" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="https" />
<data android:host="lab.web3modal.com" />
<data android:pathPattern="/flutter_appkit" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="https" />
<data android:host="dev.lab.web3modal.com" />
<data android:pathPattern="/flutter_appkit_internal" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="https" />
<data android:host="web3modal-laboratory-git-chores-addedmore-3e0f2b-walletconnect1.vercel.app" />
<data android:pathPattern="/flutter_appkit_internal" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,65 @@
package com.example.dapp

import io.flutter.embedding.android.FlutterActivity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.annotation.NonNull

import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
private val eventsChannel = "com.walletconnect.flutterdapp/events"
private val methodsChannel = "com.walletconnect.flutterdapp/methods"

private var initialLink: String? = null
private var linksReceiver: BroadcastReceiver? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val intent: Intent? = intent
initialLink = intent?.data?.toString()

EventChannel(flutterEngine?.dartExecutor?.binaryMessenger, eventsChannel).setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(args: Any?, events: EventChannel.EventSink) {
linksReceiver = createChangeReceiver(events)
}
override fun onCancel(args: Any?) {
linksReceiver = null
}
}
)

MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, methodsChannel).setMethodCallHandler { call, result ->
if (call.method == "initialLink") {
if (initialLink != null) {
result.success(initialLink)
}
}
}
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.action === Intent.ACTION_VIEW) {
linksReceiver?.onReceive(this.applicationContext, intent)
}
}

fun createChangeReceiver(events: EventChannel.EventSink): BroadcastReceiver? {
return object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val dataString = intent.dataString ?:
events.error("UNAVAILABLE", "Link unavailable", null)
events.success(dataString)
}
}
}
}
16 changes: 12 additions & 4 deletions example/dapp/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
0964B3102C494D2000AE1CDA /* Debug-internal.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Debug-internal.xcconfig"; path = "Flutter/Debug-internal.xcconfig"; sourceTree = "<group>"; };
0964B3112C494D2000AE1CDA /* Release-production.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Release-production.xcconfig"; path = "Flutter/Release-production.xcconfig"; sourceTree = "<group>"; };
0964B3122C49545400AE1CDA /* Info-internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-internal.plist"; sourceTree = "<group>"; };
09969A8C2C73BC9100B14363 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2269C513CF66C603D39509BB /* Pods-Runner.debug-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-internal.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-internal.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -128,6 +129,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
09969A8C2C73BC9100B14363 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
Expand Down Expand Up @@ -380,6 +382,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-internal";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
Expand Down Expand Up @@ -462,6 +465,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-internal";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
Expand Down Expand Up @@ -542,6 +546,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-internal";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
Expand Down Expand Up @@ -625,6 +630,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
Expand All @@ -642,7 +648,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.flutterdapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp 1721392810";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp 1724090152";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
Expand Down Expand Up @@ -761,8 +767,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = "";
Expand All @@ -778,7 +785,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.flutterdapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp 1721392810";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.walletconnect.flutterdapp";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
Expand All @@ -792,6 +799,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
Expand All @@ -809,7 +817,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.flutterdapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp 1721392810";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.walletconnect.flutterdapp 1724090152";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
Expand Down
99 changes: 92 additions & 7 deletions example/dapp/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,96 @@ import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

private static let EVENTS_CHANNEL = "com.walletconnect.flutterdapp/events"
private static let METHODS_CHANNEL = "com.walletconnect.flutterdapp/methods"

private var eventsChannel: FlutterEventChannel?
private var methodsChannel: FlutterMethodChannel?
var initialLink: String?

private let linkStreamHandler = LinkStreamHandler()

override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GeneratedPluginRegistrant.register(with: self)

let controller = window.rootViewController as! FlutterViewController
eventsChannel = FlutterEventChannel(name: AppDelegate.EVENTS_CHANNEL, binaryMessenger: controller.binaryMessenger)
eventsChannel?.setStreamHandler(linkStreamHandler)

methodsChannel = FlutterMethodChannel(name: AppDelegate.METHODS_CHANNEL, binaryMessenger: controller.binaryMessenger)
methodsChannel?.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
if (call.method == "initialLink") {
if let link = self?.initialLink {
let handled = self?.linkStreamHandler.handleLink(link)
if (handled == true) {
self?.initialLink = nil
}
}
}
})

// Add your deep link handling logic here
if let url = launchOptions?[.url] as? URL {
self.initialLink = url.absoluteString
}

if let userActivityDictionary = launchOptions?[.userActivityDictionary] as? [String: Any],
let userActivity = userActivityDictionary["UIApplicationLaunchOptionsUserActivityKey"] as? NSUserActivity,
userActivity.activityType == NSUserActivityTypeBrowsingWeb {

handleIncomingUniversalLink(userActivity: userActivity)
}

return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return linkStreamHandler.handleLink(url.absoluteString)
}

override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
handleIncomingUniversalLink(userActivity: userActivity)
return true
}
return false
}

private func handleIncomingUniversalLink(userActivity: NSUserActivity) {
if let url = userActivity.webpageURL {
// Handle the URL, navigate to appropriate screen
print("App launched with Universal Link: \(url.absoluteString)")
let handled = linkStreamHandler.handleLink(url.absoluteString)
if (!handled){
self.initialLink = url.absoluteString
}
}
}
}

class LinkStreamHandler: NSObject, FlutterStreamHandler {
var eventSink: FlutterEventSink?
var queuedLinks = [String]()

func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
self.eventSink = events
queuedLinks.forEach({ events($0) })
queuedLinks.removeAll()
return nil
}

func onCancel(withArguments arguments: Any?) -> FlutterError? {
self.eventSink = nil
return nil
}

func handleLink(_ link: String) -> Bool {
guard let eventSink = eventSink else {
queuedLinks.append(link)
return false
}
eventSink(link)
return true
}
}
12 changes: 12 additions & 0 deletions example/dapp/ios/Runner/Runner.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:lab.web3modal.com</string>
<string>applinks:dev.lab.web3modal.com</string>
<string>applinks:web3modal-laboratory-git-chores-addedmore-3e0f2b-walletconnect1.vercel.app</string>
</array>
</dict>
</plist>
Loading
Loading