Capacitor Deep Linking

Cap-go/capgo-skills/plugins/capacitor-features/skills/capacitor-deep-linking

作者 Cap-go2cfb277a148832bdb1a835b01e4de323687b498b無授權條款72 個星標收錄於 2026年10月9日更新於 2026年10月9日儲存庫今天更新

Complete guide to implementing deep links and universal links in Capacitor apps. Covers iOS Universal Links, Android App Links, custom URL schemes, and navigation handling. Use this skill when users need to open their app from links.

AI 產生的概覽

在 Capacitor 行動應用中實作深層連結、通用連結與應用程式連結的指南。

功能
此技能提供為 Capacitor 應用加入深層連結的說明,涵蓋自訂 URL scheme、iOS 通用連結與 Android 應用程式連結。內容說明如何設定 Info.plist、AndroidManifest.xml、apple-app-site-association 與 assetlinks.json 檔案,以及如何使用 Capacitor App 外掛處理傳入的 URL。也包含 React Router 與 Vue Router 的路由整合範例、OAuth 回呼處理、查詢參數解析、延遲深層連結、測試指令與常見問題排解。
適用情境
當使用者想從連結開啟其 Capacitor 應用、設定通用連結或應用程式連結、定義自訂 URL scheme,或處理分享與邀請連結時使用。除錯連結在瀏覽器開啟而非應用程式時也適用。
執行需求
需要已安裝 @capacitor/app 外掛的 Capacitor 行動專案,以及 iOS 與 Android 平台設定檔。通用連結與應用程式連結需要在應用網域上以 HTTPS 託管關聯檔案。此技能不含指令碼,僅為說明與程式碼範例。

Deep Linking in Capacitor

Implement deep links, universal links, and app links in Capacitor apps.

When to Use This Skill

  • User wants deep links
  • User needs universal links
  • User asks about URL schemes
  • User wants to open app from links
  • User needs share links

Types of Deep Links

TypePlatformFormatRequires Server
Custom URL SchemeBothmyapp://pathNo
Universal LinksiOShttps://myapp.com/pathYes
App LinksAndroidhttps://myapp.com/pathYes

Quick Start

Install Plugin

bash
npm install @capacitor/appnpx cap sync

Handle Deep Links

typescript
import { App } from '@capacitor/app';
// Listen for deep link opensApp.addListener('appUrlOpen', (event) => {  console.log('App opened with URL:', event.url);
  // Parse and navigate  const url = new URL(event.url);  handleDeepLink(url);});
function handleDeepLink(url: URL) {  // Custom scheme: myapp://product/123  // Universal link: https://myapp.com/product/123
  const path = url.pathname || url.host + url.pathname;
  // Route based on path  if (path.startsWith('/product/')) {    const productId = path.split('/')[2];    navigateTo(`/product/${productId}`);  } else if (path.startsWith('/user/')) {    const userId = path.split('/')[2];    navigateTo(`/profile/${userId}`);  } else if (path === '/login') {    navigateTo('/login');  } else {    navigateTo('/');  }}

Custom URL Scheme

iOS Configuration

xml
<!-- ios/App/App/Info.plist --><key>CFBundleURLTypes</key><array>    <dict>        <key>CFBundleURLName</key>        <string>com.yourcompany.yourapp</string>        <key>CFBundleURLSchemes</key>        <array>            <string>myapp</string>            <string>myapp-dev</string>        </array>    </dict></array>

Android Configuration

xml
<!-- android/app/src/main/AndroidManifest.xml --><activity android:name=".MainActivity">    <!-- Deep link 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="myapp" />    </intent-filter></activity>

Test Custom Scheme

bash
# iOS Simulatorxcrun simctl openurl booted "myapp://product/123"
# Androidadb shell am start -a android.intent.action.VIEW -d "myapp://product/123"

Universal Links (iOS)

1. Enable Associated Domains

In Xcode:

  1. Select App target
  2. Signing & Capabilities
    • Capability > Associated Domains
  3. Add: applinks:myapp.com

2. Create apple-app-site-association

Host at https://myapp.com/.well-known/apple-app-site-association:

json
{  "applinks": {    "apps": [],    "details": [      {        "appID": "TEAMID.com.yourcompany.yourapp",        "paths": [          "/product/*",          "/user/*",          "/invite/*",          "NOT /api/*"        ]      }    ]  }}

Requirements:

  • Served over HTTPS
  • Content-Type: application/json
  • No redirects
  • File at root domain

3. Info.plist

xml
<!-- ios/App/App/Info.plist --><key>com.apple.developer.associated-domains</key><array>    <string>applinks:myapp.com</string>    <string>applinks:www.myapp.com</string></array>

Verify Universal Links

bash
# Validate AASA filecurl -I https://myapp.com/.well-known/apple-app-site-association
# Check Apple CDN cachecurl "https://app-site-association.cdn-apple.com/a/v1/myapp.com"

App Links (Android)

1. Create assetlinks.json

Host at https://myapp.com/.well-known/assetlinks.json:

json
[  {    "relation": ["delegate_permission/common.handle_all_urls"],    "target": {      "namespace": "android_app",      "package_name": "com.yourcompany.yourapp",      "sha256_cert_fingerprints": [        "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"      ]    }  }]

Get SHA256 Fingerprint

bash
# Debug keystorekeytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
# Release keystorekeytool -list -v -keystore release.keystore -alias your-alias
# From APKkeytool -printcert -jarfile app-release.apk

2. AndroidManifest.xml

xml
<!-- android/app/src/main/AndroidManifest.xml --><activity android:name=".MainActivity">    <!-- App Links 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="myapp.com" />        <data android:pathPrefix="/product" />        <data android:pathPrefix="/user" />        <data android:pathPrefix="/invite" />    </intent-filter></activity>

Verify App Links

bash
# Validate assetlinks.jsoncurl https://myapp.com/.well-known/assetlinks.json
# Use Google's validatorhttps://developers.google.com/digital-asset-links/tools/generator
# Check link handling on deviceadb shell pm get-app-links com.yourcompany.yourapp

Advanced Routing

React Router Integration

typescript
import { App } from '@capacitor/app';import { useHistory } from 'react-router-dom';import { useEffect } from 'react';
function DeepLinkHandler() {  const history = useHistory();
  useEffect(() => {    App.addListener('appUrlOpen', (event) => {      const url = new URL(event.url);      const path = getPathFromUrl(url);
      // Navigate using React Router      history.push(path);    });
    // Check if app was opened with URL    App.getLaunchUrl().then((result) => {      if (result?.url) {        const url = new URL(result.url);        const path = getPathFromUrl(url);        history.push(path);      }    });  }, []);
  return null;}
function getPathFromUrl(url: URL): string {  // Handle both custom scheme and https  if (url.protocol === 'myapp:') {    return '/' + url.host + url.pathname;  }  return url.pathname + url.search;}

Vue Router Integration

typescript
import { App } from '@capacitor/app';import { useRouter } from 'vue-router';import { onMounted } from 'vue';
export function useDeepLinks() {  const router = useRouter();
  onMounted(async () => {    App.addListener('appUrlOpen', (event) => {      const path = parseDeepLink(event.url);      router.push(path);    });
    const launchUrl = await App.getLaunchUrl();    if (launchUrl?.url) {      const path = parseDeepLink(launchUrl.url);      router.push(path);    }  });}

Deferred Deep Links

Handle links when app wasn't installed:

typescript
import { App } from '@capacitor/app';import { Preferences } from '@capacitor/preferences';
// On first launch, check for deferred linkasync function checkDeferredDeepLink() {  const { value: isFirstLaunch } = await Preferences.get({ key: 'firstLaunch' });
  if (isFirstLaunch !== 'false') {    await Preferences.set({ key: 'firstLaunch', value: 'false' });
    // Check with your attribution service    const deferredLink = await fetchDeferredLink();    if (deferredLink) {      handleDeepLink(new URL(deferredLink));    }  }}

Query Parameters

typescript
App.addListener('appUrlOpen', (event) => {  const url = new URL(event.url);
  // Get query parameters  const source = url.searchParams.get('source');  const campaign = url.searchParams.get('campaign');  const referrer = url.searchParams.get('ref');
  // Track attribution  analytics.logEvent('deep_link_open', {    path: url.pathname,    source,    campaign,    referrer,  });
  // Navigate with state  navigateTo(url.pathname, {    state: { source, campaign, referrer },  });});

OAuth Callback Handling

typescript
// Handle OAuth redirectApp.addListener('appUrlOpen', async (event) => {  const url = new URL(event.url);
  if (url.pathname === '/oauth/callback') {    const code = url.searchParams.get('code');    const state = url.searchParams.get('state');    const error = url.searchParams.get('error');
    if (error) {      handleOAuthError(error);      return;    }
    if (code && validateState(state)) {      await exchangeCodeForToken(code);      navigateTo('/home');    }  }});

Testing

Test Matrix

ScenarioCommand
Custom schememyapp://path
Universal link cold startTap link with app closed
Universal link warm startTap link with app in background
Universal link in SafariType URL in Safari
App link cold startTap link with app closed
App link in ChromeTap link in Chrome

Debug Tools

bash
# iOS: Check associated domains entitlementcodesign -d --entitlements - App.app | grep associated-domains
# iOS: Reset Universal Links cachexcrun simctl erase all
# Android: Check verified linksadb shell dumpsys package d | grep -A5 "Package: com.yourcompany.yourapp"

Common Issues

IssueSolution
Universal Links not workingCheck AASA file, SSL, entitlements
App Links not verifiedCheck assetlinks.json, fingerprint
Links open in browserCheck intent-filter, autoVerify
Cold start not handledUse App.getLaunchUrl()
Simulator issuesReset simulator, rebuild app

Resources

來源與署名

來源:Cap-go/capgo-skills位於plugins/capacitor-features/skills/capacitor-deep-linking提交2cfb277

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架