Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dbcbd54
feat: android deeplinks
sosweetham Jul 23, 2025
ef89071
feat: explicit app link declarations
sosweetham Jul 23, 2025
d6d26e5
feat: add ios code
sosweetham Jul 23, 2025
5327809
fix: add ios deeplink adaptation
sosweetham Jul 23, 2025
b3b6db2
feat: ios working
sosweetham Jul 23, 2025
f6aae7a
fix: revert ios to prior logic
sosweetham Jul 24, 2025
24eb8b8
fix(cleanup): regen android files with old names
sosweetham Jul 24, 2025
499c10e
fix: web link criteria
sosweetham Jul 24, 2025
bf4a107
fix: conditional auto verify intent filter for android app links
sosweetham Jul 24, 2025
139f157
fix: default to true
sosweetham Jul 24, 2025
be12db2
fix: typo
sosweetham Jul 24, 2025
3b09837
fix: pnpm version
sosweetham Jul 24, 2025
340ed49
cleanup
lucasfernog Jul 24, 2025
d3bdc5c
fix: web link regression
sosweetham Jul 24, 2025
19ee934
trim androidmanifest update
lucasfernog Jul 24, 2025
0668697
fix deep link validation broken due to appLink=true default
lucasfernog Jul 24, 2025
66346ca
implement update_info_plist
lucasfernog Jul 24, 2025
c351a1a
Merge branch 'tauri-apps:v2' into feat/deep-links
sosweetham Jul 28, 2025
503c9df
Merge branch 'v2' into feat/deep-links
sosweetham Jul 28, 2025
40b4747
fix: remove old patch crates
sosweetham Jul 29, 2025
adcb24e
Merge branch 'v2' into feat/deep-links
sosweetham Jul 29, 2025
bcd73b0
fix: use latest patch tauri
sosweetham Jul 31, 2025
532788e
Merge branch 'v2' into feat/deep-links
sosweetham Jul 31, 2025
a99ee8c
Merge branch 'v2' into feat/deep-links
sosweetham Aug 7, 2025
ce3496c
Merge remote-tracking branch 'origin/v2' into feat/deep-links
lucasfernog Aug 19, 2025
ebb6afa
lint
lucasfernog Aug 19, 2025
559ef3c
Merge remote-tracking branch 'origin/v2' into feat/deep-links
lucasfernog Aug 19, 2025
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions plugins/deep-link/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ serde_json = { workspace = true }
tauri-utils = { workspace = true }
tauri-plugin = { workspace = true, features = ["build"] }

[target."cfg(target_os = \"macos\")".build-dependencies]
plist = "1"

[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
Expand Down
145 changes: 119 additions & 26 deletions plugins/deep-link/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,64 @@ const COMMANDS: &[&str] = &["get_current", "register", "unregister", "is_registe

// TODO: Consider using activity-alias in case users may have multiple activities in their app.
fn intent_filter(domain: &AssociatedDomain) -> String {
let host = domain
.host
.as_ref()
.map(|h| format!(r#"<data android:host="{h}" />"#))
.unwrap_or_default();

let auto_verify = if domain.is_app_link() {
r#"android:autoVerify="true" "#.to_string()
} else {
String::new()
};

format!(
r#"<intent-filter android:autoVerify="true">
r#"<intent-filter {auto_verify}>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
{}
<data android:host="{}" />
{}
{}
{}
{}
{schemes}
{host}
{domains}
{path_patterns}
{path_prefixes}
{path_suffixes}
</intent-filter>"#,
domain
schemes = domain
.scheme
.iter()
.map(|scheme| format!(r#"<data android:scheme="{scheme}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain.host,
domain
host = host,
domains = domain
.path
.iter()
.map(|path| format!(r#"<data android:path="{path}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain
path_patterns = domain
.path_pattern
.iter()
.map(|pattern| format!(r#"<data android:pathPattern="{pattern}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain
path_prefixes = domain
.path_prefix
.iter()
.map(|prefix| format!(r#"<data android:pathPrefix="{prefix}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain
path_suffixes = domain
.path_suffix
.iter()
.map(|suffix| format!(r#"<data android:pathSuffix="{suffix}" />"#))
.collect::<Vec<_>>()
.join("\n "),
)
.trim()
.to_string()
}

fn main() {
Expand All @@ -68,6 +82,16 @@ fn main() {
}

if let Some(config) = tauri_plugin::plugin_config::<Config>("deep-link") {
let errors: Vec<String> = config
.mobile
.iter()
.filter_map(|d| d.validate().err())
.collect();

if !errors.is_empty() {
panic!("Deep link config validation failed:\n{}", errors.join("\n"));
}

tauri_plugin::mobile::update_android_manifest(
"DEEP LINK PLUGIN",
"activity",
Expand All @@ -80,20 +104,89 @@ fn main() {
)
.expect("failed to rewrite AndroidManifest.xml");

#[cfg(target_os = "macos")]
#[cfg(any(target_os = "macos", target_os = "ios"))]
{
tauri_plugin::mobile::update_entitlements(|entitlements| {
entitlements.insert(
"com.apple.developer.associated-domains".into(),
config
.mobile
.into_iter()
.map(|d| format!("applinks:{}", d.host).into())
.collect::<Vec<_>>()
.into(),
);
})
.expect("failed to update entitlements");
// we need to ensure that the entitlements are only
// generated for explicit app links and not
// other deep links because then they
// are just going to complain and not be built or signed
let has_app_links = config.mobile.iter().any(|d| d.is_app_link());

if !has_app_links {
tauri_plugin::mobile::update_entitlements(|entitlements| {
entitlements.remove("com.apple.developer.associated-domains");
})
.expect("failed to update entitlements");
} else {
tauri_plugin::mobile::update_entitlements(|entitlements| {
entitlements.insert(
"com.apple.developer.associated-domains".into(),
config
.mobile
.iter()
.filter(|d| d.is_app_link())
.filter_map(|d| d.host.as_ref())
.map(|host| format!("applinks:{}", host).into())
.collect::<Vec<_>>()
.into(),
);
})
.expect("failed to update entitlements");
}

let deep_link_domains = config
.mobile
.iter()
.filter_map(|domain| {
if domain.is_app_link() {
return None;
}

Some(domain)
})
.collect::<Vec<_>>();

if deep_link_domains.is_empty() {
tauri_plugin::mobile::update_info_plist(|info_plist| {
info_plist.remove("CFBundleURLTypes");
})
.expect("failed to update Info.plist");
} else {
tauri_plugin::mobile::update_info_plist(|info_plist| {
info_plist.insert(
"CFBundleURLTypes".into(),
deep_link_domains
.iter()
.map(|domain| {
let schemes = domain
.scheme
.iter()
.filter(|scheme| {
scheme.as_str() != "https" && scheme.as_str() != "http"
})
.collect::<Vec<_>>();

let mut dict = plist::Dictionary::new();
dict.insert(
"CFBundleURLSchemes".into(),
schemes
.iter()
.map(|s| s.to_string().into())
.collect::<Vec<_>>()
.into(),
);
dict.insert(
"CFBundleURLName".into(),
format!("{}", domain.scheme[0]).into(),
);
plist::Value::Dictionary(dict)
})
.collect::<Vec<_>>()
.into(),
);
})
.expect("failed to update Info.plist");
}
}
}
}
2 changes: 1 addition & 1 deletion plugins/deep-link/examples/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"@tauri-apps/api": "2.8.0",
"@tauri-apps/plugin-deep-link": "2.4.1"
"@tauri-apps/plugin-deep-link": "link:../.."
},
"devDependencies": {
"@tauri-apps/cli": "2.8.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ val tauriProperties = Properties().apply {
}

android {
compileSdk = 34
compileSdk = 36
namespace = "com.tauri.deep_link_example"
defaultConfig {
manifestPlaceholders["usesCleartextTraffic"] = "false"
applicationId = "com.tauri.deep_link_example"
minSdk = 24
targetSdk = 34
targetSdk = 36
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
}
Expand Down Expand Up @@ -58,9 +58,10 @@ rust {
}

dependencies {
implementation("androidx.webkit:webkit:1.6.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.webkit:webkit:1.14.0")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("androidx.activity:activity-ktx:1.10.1")
implementation("com.google.android.material:material:1.12.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.4")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,40 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
<intent-filter android:autoVerify="true">
<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="http" />
<data android:scheme="https" />
<data android:scheme="http" />
<data android:host="fabianlars.de" />


<data android:pathPrefix="/intent" />

</intent-filter>
<intent-filter android:autoVerify="true">
<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="http" />
<data android:scheme="https" />
<data android:scheme="http" />
<data android:host="tauri.app" />




</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="taurideeplink" />





</intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
</activity>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

package com.tauri.deep_link_example

class MainActivity : TauriActivity()
import android.os.Bundle
import androidx.activity.enableEdgeToEdge

class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.5.1")
classpath("com.android.tools.build:gradle:8.11.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ repositories {

dependencies {
compileOnly(gradleApi())
implementation("com.android.tools.build:gradle:8.5.1")
implementation("com.android.tools.build:gradle:8.11.0")
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Tue May 10 19:22:52 CST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
Loading
Loading