Deep Links
Handle Universal Links in your iOS app to route taps into the right screen, with cold start and warm start covered by the WarpLink SDK.
When a user taps a WarpLink URL and your app is installed, iOS opens your app via Universal Links. The SDK resolves the URL into a WarpLinkDeepLink with the destination, custom parameters, and attribution data, then delivers it to the onLink callback you passed to configure(). This covers both cold start and warm start.
Deliver Incoming URLs to the SDK
You register onLink once in configure() (see iOS SDK). The only remaining job is handing tapped URLs to the SDK. Choose one, no swizzling:
Set your scene delegate class to WarpLinkSceneDelegate (or your app delegate to WarpLinkAppDelegate). The SDK captures Universal Links and dispatches resolved links to onLink automatically.
// Info.plist scene configuration, or:
class SceneDelegate: WarpLinkSceneDelegate {
// Your own scene code. WarpLink forwards Universal Links for you.
}Keep your own delegate and forward the incoming activity or URL:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
WarpLink.continue(userActivity)
}
}In SwiftUI, use .onOpenURL { WarpLink.open($0) } or .onContinueUserActivity(...) { WarpLink.continue($0) }.
WarpLink.open(_:) and WarpLink.continue(_:) return a Bool you can ignore or act on. They return true when the URL is a WarpLink link the SDK will handle: a WarpLink domain and a path of exactly one segment, which is what a slug always is. aplnk.to always counts; a custom domain counts once you declare it, which is what makes the answer right on the first launch. By default your app is associated with every path on the domain, so other paths open your app too. The SDK returns false for those and does nothing with them, leaving your own router free to take over:
.onOpenURL { url in
if !WarpLink.open(url) {
myRouter.handle(url) // not a WarpLink slug, e.g. /settings/profile
}
}
The onLink callback is always called on the main thread, so you can safely update UI from it.
Cold Start vs Warm Start
| Scenario | Entry Point | Behavior |
|---|---|---|
| Cold start | App not running: system launches it | The forwarded URL resolves once the app is ready, then onLink fires |
| Warm start | App in background: brought to foreground | The forwarded URL resolves immediately, then onLink fires |
Both cases flow through the same onLink callback. The SDK resolves the link by calling the WarpLink API.
Working with Deep Link Data
Your onLink callback receives a WarpLinkDeepLink:
WarpLinkOptions(onLink: { result in
if case .success(let deepLink) = result, let deepLink {
print("Link ID: \(deepLink.linkId)")
print("Destination: \(deepLink.destination)")
print("Deferred: \(deepLink.isDeferred)")
// iOS-specific deep link URL (e.g., myapp://product/123)
if let deepLinkUrl = deepLink.deepLinkUrl {
navigateToPath(deepLinkUrl)
}
// Custom parameters attached to the link
if let productId = deepLink.customParams["product_id"]?.stringValue {
showProduct(id: productId)
}
}
})
Manual Resolution (Advanced)
To drive resolution yourself, set autoDeepLinkHandling: false in WarpLinkOptions and call handleDeepLink with the URL you receive from the OS:
WarpLink.handleDeepLink(url) { result in
if case .success(let deepLink) = result {
navigateTo(deepLink.destination)
}
}
Error Handling
WarpLink.handleDeepLink(url) { result in
switch result {
case .success(let deepLink):
navigateTo(deepLink.destination)
case .failure(let error):
switch error {
case .notConfigured:
// SDK not initialized — call configure() first
break
case .invalidURL:
// Not a WarpLink domain (aplnk.to or one you declared)
break
case .linkNotFound:
// Link deleted or expired
showLinkExpired()
case .networkError:
// No connectivity — retry or show offline state
showOfflineMessage()
case .serverError(let code, let message):
print("Server error \(code): \(message)")
default:
print("Error: \(error.localizedDescription)")
}
}
}
Verify AASA Configuration
WarpLink generates the Apple App Site Association file automatically. Verify it's served correctly:
curl https://aplnk.to/.well-known/apple-app-site-association
The response should contain your Team ID and Bundle ID in the applinks section.
After adding Associated Domains, you may need to delete and reinstall the app for iOS to re-fetch the AASA file.