jpskill.com
🛠️ 開発・MCP コミュニティ

deep-linking

Android App LinksやiOS Universal Linksといったモバイルアプリのディープリンク技術を使い、アプリ内の特定画面へ直接誘導したり、インストール遅延後の連携を実現したりするSkill。

📜 元の英語説明(参考)

Deep linking patterns for mobile - Android App Links, iOS Universal Links, intent filters, URI handling, deferred deep links, and navigation integration.

🇯🇵 日本人クリエイター向け解説

一言でいうと

Android App LinksやiOS Universal Linksといったモバイルアプリのディープリンク技術を使い、アプリ内の特定画面へ直接誘導したり、インストール遅延後の連携を実現したりするSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o deep-linking.zip https://jpskill.com/download/16409.zip && unzip -o deep-linking.zip && rm deep-linking.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16409.zip -OutFile "$d\deep-linking.zip"; Expand-Archive "$d\deep-linking.zip" -DestinationPath $d -Force; ri "$d\deep-linking.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して deep-linking.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → deep-linking フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

モバイル向けのディープリンクパターン

URIスキームの設計

実装前に一貫性のある URI スキームを設計します。

myapp://                          # アプリのルート
myapp://home                      # ホーム画面
myapp://product/{id}              # 製品詳細
myapp://profile/{userId}          # ユーザープロフィール
myapp://settings/notifications    # ネストされた設定
myapp://search?q={query}          # クエリパラメータ

パスは RESTful で小文字、かつ予測可能に保ちます。パスセグメントは階層構造に、クエリパラメータはフィルタに使用します。


Android

AndroidManifest.xml の Intent Filter

<activity
    android:name=".MainActivity"
    android:exported="true">

    <!-- Custom URI scheme -->
    <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"
            android:host="product"
            android:pathPrefix="/" />
    </intent-filter>

    <!-- App Links (HTTPS verified) -->
    <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"
            android:host="www.myapp.com"
            android:pathPrefix="/product" />
    </intent-filter>
</activity>

assetlinks.json を使用した App Links

https://www.myapp.com/.well-known/assetlinks.json にホストします。

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.myapp.android",
    "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"
    ]
  }
}]

証明書のフィンガープリントを取得します。

keytool -list -v -keystore my-release-key.keystore -alias my-key-alias

Activity でのディープリンクの処理

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleDeepLink(intent)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        handleDeepLink(intent)
    }

    private fun handleDeepLink(intent: Intent) {
        val uri = intent.data ?: return
        val path = uri.path ?: return
        val segments = path.split("/").filter { it.isNotEmpty() }

        when {
            segments.firstOrNull() == "product" -> {
                val productId = segments.getOrNull(1)
                navigateToProduct(productId)
            }
            segments.firstOrNull() == "profile" -> {
                val userId = segments.getOrNull(1)
                navigateToProfile(userId)
            }
        }
    }
}

Compose Navigation Deep Link の統合

NavHost(navController, startDestination = "home") {
    composable(
        route = "product/{productId}",
        arguments = listOf(navArgument("productId") { type = NavType.StringType }),
        deepLinks = listOf(
            navDeepLink { uriPattern = "myapp://product/{productId}" },
            navDeepLink { uriPattern = "https://www.myapp.com/product/{productId}" }
        )
    ) { backStackEntry ->
        val productId = backStackEntry.arguments?.getString("productId")
        ProductScreen(productId = productId)
    }
}

Deferred Deep Links (インストールアトリビューション)

アプリのインストール後も有効な遅延ディープリンクには、Firebase Dynamic Links または AppsFlyer を使用します。

Firebase.dynamicLinks
    .getDynamicLink(intent)
    .addOnSuccessListener { pendingDynamicLinkData ->
        val deepLink: Uri? = pendingDynamicLinkData?.link
        deepLink?.let { handleDeepLink(it) }
    }

iOS

apple-app-site-association を使用した Universal Links

https://www.myapp.com/.well-known/apple-app-site-association にホストします。

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAMID.com.myapp.ios",
        "paths": ["/product/*", "/profile/*", "/settings/*"]
      }
    ]
  }
}

Xcode で Associated Domains を有効にします: applinks:www.myapp.com

Info.plist の URL Schemes

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
        <key>CFBundleURLName</key>
        <string>com.myapp.ios</string>
    </dict>
</array>

AppDelegate / SceneDelegate での処理

// SceneDelegate (iOS 13+)
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else { return }
    DeepLinkRouter.shared.handle(url: url)
}

// Universal Links
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    guard let url = userActivity.webpageURL else { return }
    DeepLinkRouter.shared.handle(url: url)
}

SwiftUI .onOpenURL

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    DeepLinkRouter.shared.handle(url: url)
                }
        }
    }
}

ディープリンクのルーティングアーキテクチャ

// iOS Router
final class DeepLinkRouter: ObservableObject {
    static let shared = DeepLinkRouter()
    @Published var destination: Destination?

    enum Destination: Equatable {
        case product(id: String)
        case profile(userId: String)
        case settings
    }

    func handle(url: URL) {
        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return }
        let pathSegments = components.path.split(separator: "/").map(String.init)

        switch pathSegments.first {
        case "product":
            destination = .product(id: pathSegmen
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Deep Linking Patterns for Mobile

URI Scheme Design

Design a consistent URI scheme before implementation:

myapp://                          # App root
myapp://home                      # Home screen
myapp://product/{id}              # Product detail
myapp://profile/{userId}          # User profile
myapp://settings/notifications    # Nested settings
myapp://search?q={query}          # Query parameters

Keep paths RESTful, lowercase, and predictable. Use path segments for hierarchy and query params for filters.


Android

Intent Filter in AndroidManifest.xml

<activity
    android:name=".MainActivity"
    android:exported="true">

    <!-- Custom URI scheme -->
    <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"
            android:host="product"
            android:pathPrefix="/" />
    </intent-filter>

    <!-- App Links (HTTPS verified) -->
    <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"
            android:host="www.myapp.com"
            android:pathPrefix="/product" />
    </intent-filter>
</activity>

App Links with assetlinks.json

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

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.myapp.android",
    "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 your certificate fingerprint:

keytool -list -v -keystore my-release-key.keystore -alias my-key-alias

Handling Deep Links in Activity

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleDeepLink(intent)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        handleDeepLink(intent)
    }

    private fun handleDeepLink(intent: Intent) {
        val uri = intent.data ?: return
        val path = uri.path ?: return
        val segments = path.split("/").filter { it.isNotEmpty() }

        when {
            segments.firstOrNull() == "product" -> {
                val productId = segments.getOrNull(1)
                navigateToProduct(productId)
            }
            segments.firstOrNull() == "profile" -> {
                val userId = segments.getOrNull(1)
                navigateToProfile(userId)
            }
        }
    }
}

Compose Navigation Deep Link Integration

NavHost(navController, startDestination = "home") {
    composable(
        route = "product/{productId}",
        arguments = listOf(navArgument("productId") { type = NavType.StringType }),
        deepLinks = listOf(
            navDeepLink { uriPattern = "myapp://product/{productId}" },
            navDeepLink { uriPattern = "https://www.myapp.com/product/{productId}" }
        )
    ) { backStackEntry ->
        val productId = backStackEntry.arguments?.getString("productId")
        ProductScreen(productId = productId)
    }
}

Deferred Deep Links (Install Attribution)

Use Firebase Dynamic Links or AppsFlyer for deferred deep links that survive app install:

Firebase.dynamicLinks
    .getDynamicLink(intent)
    .addOnSuccessListener { pendingDynamicLinkData ->
        val deepLink: Uri? = pendingDynamicLinkData?.link
        deepLink?.let { handleDeepLink(it) }
    }

iOS

Universal Links with apple-app-site-association

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

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAMID.com.myapp.ios",
        "paths": ["/product/*", "/profile/*", "/settings/*"]
      }
    ]
  }
}

Enable Associated Domains in Xcode: applinks:www.myapp.com

URL Schemes in Info.plist

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
        <key>CFBundleURLName</key>
        <string>com.myapp.ios</string>
    </dict>
</array>

Handling in AppDelegate / SceneDelegate

// SceneDelegate (iOS 13+)
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else { return }
    DeepLinkRouter.shared.handle(url: url)
}

// Universal Links
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    guard let url = userActivity.webpageURL else { return }
    DeepLinkRouter.shared.handle(url: url)
}

SwiftUI .onOpenURL

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    DeepLinkRouter.shared.handle(url: url)
                }
        }
    }
}

Deep Link Routing Architecture

// iOS Router
final class DeepLinkRouter: ObservableObject {
    static let shared = DeepLinkRouter()
    @Published var destination: Destination?

    enum Destination: Equatable {
        case product(id: String)
        case profile(userId: String)
        case settings
    }

    func handle(url: URL) {
        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return }
        let pathSegments = components.path.split(separator: "/").map(String.init)

        switch pathSegments.first {
        case "product":
            destination = .product(id: pathSegments[safe: 1] ?? "")
        case "profile":
            destination = .profile(userId: pathSegments[safe: 1] ?? "")
        default:
            break
        }
    }
}

Testing Deep Links

# Android - test custom scheme
adb shell am start -a android.intent.action.VIEW -d "myapp://product/123"

# Android - test App Links
adb shell am start -a android.intent.action.VIEW -d "https://www.myapp.com/product/123"

# iOS Simulator - test URL scheme
xcrun simctl openurl booted "myapp://product/123"

# iOS Simulator - test Universal Link
xcrun simctl openurl booted "https://www.myapp.com/product/123"

Analytics Tracking for Deep Links

Track deep link opens with source attribution:

fun trackDeepLinkOpen(uri: Uri, source: String) {
    analytics.logEvent("deep_link_opened") {
        param("uri", uri.toString())
        param("source", source) // "notification", "email", "social", "qr_code"
        param("path", uri.path ?: "")
        param("has_referrer", (uri.getQueryParameter("ref") != null).toString())
    }
}