capacitor-deep-linking
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.
What this skill does
# 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
| Type | Platform | Format | Requires Server |
|------|----------|--------|-----------------|
| Custom URL Scheme | Both | `myapp://path` | No |
| Universal Links | iOS | `https://myapp.com/path` | Yes |
| App Links | Android | `https://myapp.com/path` | Yes |
## Quick Start
### Install Plugin
```bash
npm install @capacitor/app
npx cap sync
```
### Handle Deep Links
```typescript
import { App } from '@capacitor/app';
// Listen for deep link opens
App.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 Simulator
xcrun simctl openurl booted "myapp://product/123"
# Android
adb 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
3. + Capability > Associated Domains
4. 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 file
curl -I https://myapp.com/.well-known/apple-app-site-association
# Check Apple CDN cache
curl "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 keystore
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
# Release keystore
keytool -list -v -keystore release.keystore -alias your-alias
# From APK
keytool -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.json
curl https://myapp.com/.well-known/assetlinks.json
# Use Google's validator
https://developers.google.com/digital-asset-links/tools/generator
# Check link handling on device
adb 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 link
async 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 HandRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.