App Clips 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.
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 Universal Links vs App Clips. For deep linking on Android (Instant Apps), see Android Instant Apps and deep linking.
How App Clips Use Deep Links
The Routing Logic
When a user taps a URL associated with your app:
URL tapped (e.g., https://yourapp.com/order/store-123)
→ iOS checks: is the full app installed?
→ Yes: open the full app via Universal Links
→ No: is there a registered App Clip for this URL?
→ Yes: download and launch the App Clip (~10MB)
→ No: open the URL in Safari
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.
App Clip URL Configuration
Register your App Clip URLs in App Store Connect and your apple-app-site-association file:
{
"appclips": {
"apps": ["TEAM_ID.com.yourapp.Clip"]
},
"applinks": {
"apps": [],
"details": [
{
"appIDs": ["TEAM_ID.com.yourapp"],
"components": [
{ "/": "/order/*" },
{ "/": "/menu/*" },
{ "/": "/products/*" }
]
}
]
}
}
Building an App Clip with Deep Link Support
App Clip Target Setup
An App Clip is a separate target in your Xcode project that shares code with the full app:
// AppClip/AppClipApp.swift
import SwiftUI
@main
struct OrderClip: App {
var body: some Scene {
WindowGroup {
ClipContentView()
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in
guard let url = activity.webpageURL else { return }
handleDeepLink(url)
}
}
}
func handleDeepLink(_ url: URL) {
let pathComponents = url.pathComponents
if pathComponents.contains("order"), let storeId = pathComponents.last {
NavigationState.shared.navigate(to: .orderFromStore(storeId))
} else if pathComponents.contains("menu"), let menuId = pathComponents.last {
NavigationState.shared.navigate(to: .viewMenu(menuId))
}
}
}
Invocation URL Handling
The App Clip receives the invocation URL through NSUserActivity. Parse it the same way you parse Universal Links in the full app:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else { return }
// Extract context from the URL
let context = DeepLinkParser.parse(url)
switch context {
case .storeOrder(let storeId):
showOrderFlow(storeId: storeId)
case .productView(let productId):
showProduct(productId: productId)
case .menuView(let menuId):
showMenu(menuId: menuId)
default:
showDefaultExperience()
}
}
}
Shared Code Between App and Clip
Since both the full app and App Clip handle the same URLs, share the deep link parsing logic:
// Shared/DeepLinkParser.swift (included in both targets)
enum DeepLinkDestination {
case storeOrder(String)
case productView(String)
case menuView(String)
case unknown
}
struct DeepLinkParser {
static func parse(_ url: URL) -> DeepLinkDestination {
let path = url.pathComponents
if path.contains("order"), let id = path.last, id != "order" {
return .storeOrder(id)
}
if path.contains("products"), let id = path.last, id != "products" {
return .productView(id)
}
if path.contains("menu"), let id = path.last, id != "menu" {
return .menuView(id)
}
return .unknown
}
}
App Clip Invocation Sources
NFC Tags
NFC tags at physical locations can trigger App Clips. Encode your deep link URL in the NFC tag:
NFC tag at restaurant table → https://yourapp.com/order/restaurant-abc/table-5
→ App Clip launches with table context pre-loaded
→ User sees menu and can order immediately
QR Codes
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:
QR code on parking meter → https://yourapp.com/parking/meter-1234
→ App Clip launches with meter pre-selected
→ User taps "Pay" and uses Apple Pay
App Clip Codes
App Clip Codes 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.
Maps and Siri Suggestions
If your app has a physical presence (restaurants, stores, services), register your locations in Apple Business Register. Users see your App Clip in Maps when viewing your location.
Transitioning from App Clip to Full App
Preserving User Data
When a user upgrades from the App Clip to the full app, preserve their data using a shared App Group:
// In both App Clip and full app
let sharedDefaults = UserDefaults(suiteName: "group.com.yourapp.shared")
// App Clip: save order history
func saveOrderToSharedStorage(_ order: Order) {
var orders = sharedDefaults?.array(forKey: "orderHistory") as? [[String: Any]] ?? []
orders.append(order.toDictionary())
sharedDefaults?.set(orders, forKey: "orderHistory")
}
// Full app: restore order history from App Clip
func restoreOrderHistory() -> [Order] {
guard let data = sharedDefaults?.array(forKey: "orderHistory") as? [[String: Any]] else {
return []
}
return data.compactMap { Order(dictionary: $0) }
}
Deep Link Continuity
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.
App Clip Limitations
App Clips have constraints that affect your deep link strategy:
| Limitation | Impact |
|---|---|
| 15MB size limit (was 10MB, increased in iOS 16) | Keep the App Clip focused on one task |
| No background processing | Cannot pre-fetch deep link content in background |
| Limited API access | No HealthKit, CallKit, or certain sensor APIs |
| 8-hour active period | App Clip is removed after inactivity (data preserved in App Group) |
| No push notifications without permission | Cannot re-engage users without explicit opt-in |
URL-Specific Experiences
Each App Clip invocation URL should lead to a focused experience:
/order/store-123 → ordering flow for store 123
/menu/restaurant-abc → menu viewer for restaurant abc
/parking/meter-1234 → parking payment for meter 1234
Do not try to replicate the full app in the App Clip. Each URL pattern should map to one specific task.
Tolinku and App Clips
Tolinku manages the Universal Link routing that powers both your full app and App Clip. Configure routes in the Tolinku dashboard, and Tolinku serves the apple-app-site-association 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.
For more on deep linking trends, see the future of mobile deep linking.
Get deep linking tips in your inbox
One email per week. No spam.