{"id":1536,"date":"2026-06-21T13:00:00","date_gmt":"2026-06-21T18:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1536"},"modified":"2026-03-07T03:49:32","modified_gmt":"2026-03-07T08:49:32","slug":"deep-linking-app-clips","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/deep-linking-app-clips\/","title":{"rendered":"Deep Linking and App Clips: Instant Experiences"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/developer.apple.com\/app-clips\/\" rel=\"nofollow noopener\" target=\"_blank\">App Clips<\/a> let users access a small part of your app without installing the full app. They launch from URLs, NFC tags, QR codes, or Maps, and they use the same Universal Links infrastructure that powers deep linking. This means App Clips and deep links are not separate features; they are the same routing system with different destinations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers how to build App Clips that work with your deep link strategy. For the comparison between Universal Links and App Clips, see <a href=\"https:\/\/tolinku.com\/blog\/universal-links-vs-app-clips\/\">Universal Links vs App Clips<\/a>. For deep linking on Android (Instant Apps), see <a href=\"https:\/\/tolinku.com\/blog\/android-instant-apps-deep-links\/\">Android Instant Apps and deep linking<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How App Clips Use Deep Links<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The Routing Logic<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When a user taps a URL associated with your app:<\/p>\n\n\n\n<pre><code>URL tapped (e.g., https:\/\/yourapp.com\/order\/store-123)\n  \u2192 iOS checks: is the full app installed?\n    \u2192 Yes: open the full app via Universal Links\n    \u2192 No: is there a registered App Clip for this URL?\n      \u2192 Yes: download and launch the App Clip (~10MB)\n      \u2192 No: open the URL in Safari\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The URL is the same for both the full app and the App Clip. You do not need separate URLs. iOS handles the routing based on what is installed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">App Clip URL Configuration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Register your App Clip URLs in App Store Connect and your <code>apple-app-site-association<\/code> file:<\/p>\n\n\n\n<pre><code class=\"language-json\">{\n  &quot;appclips&quot;: {\n    &quot;apps&quot;: [&quot;TEAM_ID.com.yourapp.Clip&quot;]\n  },\n  &quot;applinks&quot;: {\n    &quot;apps&quot;: [],\n    &quot;details&quot;: [\n      {\n        &quot;appIDs&quot;: [&quot;TEAM_ID.com.yourapp&quot;],\n        &quot;components&quot;: [\n          { &quot;\/&quot;: &quot;\/order\/*&quot; },\n          { &quot;\/&quot;: &quot;\/menu\/*&quot; },\n          { &quot;\/&quot;: &quot;\/products\/*&quot; }\n        ]\n      }\n    ]\n  }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Building an App Clip with Deep Link Support<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">App Clip Target Setup<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">An App Clip is a separate target in your Xcode project that shares code with the full app:<\/p>\n\n\n\n<pre><code class=\"language-swift\">\/\/ AppClip\/AppClipApp.swift\nimport SwiftUI\n\n@main\nstruct OrderClip: App {\n    var body: some Scene {\n        WindowGroup {\n            ClipContentView()\n                .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in\n                    guard let url = activity.webpageURL else { return }\n                    handleDeepLink(url)\n                }\n        }\n    }\n\n    func handleDeepLink(_ url: URL) {\n        let pathComponents = url.pathComponents\n\n        if pathComponents.contains(&quot;order&quot;), let storeId = pathComponents.last {\n            NavigationState.shared.navigate(to: .orderFromStore(storeId))\n        } else if pathComponents.contains(&quot;menu&quot;), let menuId = pathComponents.last {\n            NavigationState.shared.navigate(to: .viewMenu(menuId))\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Invocation URL Handling<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The App Clip receives the invocation URL through <code>NSUserActivity<\/code>. Parse it the same way you parse Universal Links in the full app:<\/p>\n\n\n\n<pre><code class=\"language-swift\">class SceneDelegate: UIResponder, UIWindowSceneDelegate {\n    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {\n        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,\n              let url = userActivity.webpageURL else { return }\n\n        \/\/ Extract context from the URL\n        let context = DeepLinkParser.parse(url)\n\n        switch context {\n        case .storeOrder(let storeId):\n            showOrderFlow(storeId: storeId)\n        case .productView(let productId):\n            showProduct(productId: productId)\n        case .menuView(let menuId):\n            showMenu(menuId: menuId)\n        default:\n            showDefaultExperience()\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Shared Code Between App and Clip<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Since both the full app and App Clip handle the same URLs, share the deep link parsing logic:<\/p>\n\n\n\n<pre><code class=\"language-swift\">\/\/ Shared\/DeepLinkParser.swift (included in both targets)\nenum DeepLinkDestination {\n    case storeOrder(String)\n    case productView(String)\n    case menuView(String)\n    case unknown\n}\n\nstruct DeepLinkParser {\n    static func parse(_ url: URL) -&gt; DeepLinkDestination {\n        let path = url.pathComponents\n\n        if path.contains(&quot;order&quot;), let id = path.last, id != &quot;order&quot; {\n            return .storeOrder(id)\n        }\n        if path.contains(&quot;products&quot;), let id = path.last, id != &quot;products&quot; {\n            return .productView(id)\n        }\n        if path.contains(&quot;menu&quot;), let id = path.last, id != &quot;menu&quot; {\n            return .menuView(id)\n        }\n\n        return .unknown\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">App Clip Invocation Sources<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">NFC Tags<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">NFC tags at physical locations can trigger App Clips. Encode your deep link URL in the NFC tag:<\/p>\n\n\n\n<pre><code>NFC tag at restaurant table \u2192 https:\/\/yourapp.com\/order\/restaurant-abc\/table-5\n  \u2192 App Clip launches with table context pre-loaded\n  \u2192 User sees menu and can order immediately\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">QR Codes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">QR codes work the same way as any URL invocation. The QR code encodes a URL; iOS checks if an App Clip is registered for that URL:<\/p>\n\n\n\n<pre><code>QR code on parking meter \u2192 https:\/\/yourapp.com\/parking\/meter-1234\n  \u2192 App Clip launches with meter pre-selected\n  \u2192 User taps &quot;Pay&quot; and uses Apple Pay\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">App Clip Codes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/app-clip-codes\" rel=\"nofollow noopener\" target=\"_blank\">App Clip Codes<\/a> are Apple-designed visual codes that combine NFC and visual scanning. They are more reliable than QR codes for App Clip invocation because iOS recognizes them natively.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Maps and Siri Suggestions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If your app has a physical presence (restaurants, stores, services), register your locations in <a href=\"https:\/\/register.apple.com\/business\" rel=\"nofollow noopener\" target=\"_blank\">Apple Business Register<\/a>. Users see your App Clip in Maps when viewing your location.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Transitioning from App Clip to Full App<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Preserving User Data<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When a user upgrades from the App Clip to the full app, preserve their data using a shared <a href=\"https:\/\/developer.apple.com\/documentation\/bundleresources\/entitlements\/com_apple_security_application-groups\" rel=\"nofollow noopener\" target=\"_blank\">App Group<\/a>:<\/p>\n\n\n\n<pre><code class=\"language-swift\">\/\/ In both App Clip and full app\nlet sharedDefaults = UserDefaults(suiteName: &quot;group.com.yourapp.shared&quot;)\n\n\/\/ App Clip: save order history\nfunc saveOrderToSharedStorage(_ order: Order) {\n    var orders = sharedDefaults?.array(forKey: &quot;orderHistory&quot;) as? [[String: Any]] ?? []\n    orders.append(order.toDictionary())\n    sharedDefaults?.set(orders, forKey: &quot;orderHistory&quot;)\n}\n\n\/\/ Full app: restore order history from App Clip\nfunc restoreOrderHistory() -&gt; [Order] {\n    guard let data = sharedDefaults?.array(forKey: &quot;orderHistory&quot;) as? [[String: Any]] else {\n        return []\n    }\n    return data.compactMap { Order(dictionary: $0) }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Deep Link Continuity<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When the user installs the full app, the same URLs that triggered the App Clip now open the full app. No URL changes needed. This is the key advantage of using Universal Links as the foundation for both.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">App Clip Limitations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">App Clips have constraints that affect your deep link strategy:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Limitation<\/th>\n<th>Impact<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>15MB size limit (was 10MB, increased in iOS 16)<\/td>\n<td>Keep the App Clip focused on one task<\/td>\n<\/tr>\n<tr>\n<td>No background processing<\/td>\n<td>Cannot pre-fetch deep link content in background<\/td>\n<\/tr>\n<tr>\n<td>Limited API access<\/td>\n<td>No HealthKit, CallKit, or certain sensor APIs<\/td>\n<\/tr>\n<tr>\n<td>8-hour active period<\/td>\n<td>App Clip is removed after inactivity (data preserved in App Group)<\/td>\n<\/tr>\n<tr>\n<td>No push notifications without permission<\/td>\n<td>Cannot re-engage users without explicit opt-in<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">URL-Specific Experiences<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Each App Clip invocation URL should lead to a focused experience:<\/p>\n\n\n\n<pre><code>\/order\/store-123        \u2192 ordering flow for store 123\n\/menu\/restaurant-abc    \u2192 menu viewer for restaurant abc\n\/parking\/meter-1234     \u2192 parking payment for meter 1234\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not try to replicate the full app in the App Clip. Each URL pattern should map to one specific task.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tolinku and App Clips<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/deep-linking\">Tolinku<\/a> manages the Universal Link routing that powers both your full app and App Clip. Configure routes in the <a href=\"https:\/\/tolinku.com\/docs\/concepts\/deep-linking\/\">Tolinku dashboard<\/a>, and Tolinku serves the <code>apple-app-site-association<\/code> file with both your full app and App Clip bundle IDs. The same deep link URL works for Universal Links, App Clips, and web fallback.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For more on deep linking trends, see <a href=\"https:\/\/tolinku.com\/blog\/future-mobile-deep-linking\/\">the future of mobile deep linking<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Combine deep linking with Apple App Clips. Create instant app experiences that bridge deep links with lightweight, installable app clips.<\/p>\n","protected":false},"author":2,"featured_media":1535,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Deep Linking and App Clips: Instant Experiences","rank_math_description":"Combine deep linking with Apple App Clips. Create instant app experiences that bridge deep links with deep link routing.","rank_math_focus_keyword":"deep linking App Clips","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-deep-linking-app-clips.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-deep-linking-app-clips.png","footnotes":""},"categories":[11],"tags":[66,122,20,67,24,69,22,33],"class_list":["post-1536","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deep-linking","tag-app-clips","tag-apple","tag-deep-linking","tag-instant-apps","tag-ios","tag-mobile-development","tag-universal-links","tag-user-experience"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1536","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=1536"}],"version-history":[{"count":3,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1536\/revisions"}],"predecessor-version":[{"id":2626,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1536\/revisions\/2626"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1535"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1536"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1536"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1536"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}