Swift SDK for Dynalinks deferred deep linking.
- iOS 16.0+
- Swift 5.7+
- Xcode 15.0+
Add the following to your Podfile:
pod 'DynalinksSDK', '~> 1.0'Then run:
pod installAdd the following to your Package.swift:
dependencies: [
.package(url: "https://github.com/dynalinks/dynalinks-ios-sdk", from: "1.0.0")
]Or in Xcode:
- Go to File > Add Package Dependencies
- Enter the repository URL:
https://github.com/dynalinks/dynalinks-ios-sdk - Select the version and add to your target
Configure the SDK as early as possible in your app's lifecycle.
import DynalinksSDK
@main
struct MyApp: App {
init() {
do {
try Dynalinks.configure(clientAPIKey: "your-client-api-key")
} catch {
print("Failed to configure Dynalinks: \(error)")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}Or in UIKit:
import DynalinksSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
do {
try Dynalinks.configure(clientAPIKey: "your-client-api-key")
} catch {
print("Failed to configure Dynalinks: \(error)")
}
return true
}
}Check for deferred deep links on first app launch:
import DynalinksSDK
struct ContentView: View {
var body: some View {
Text("Hello, World!")
.task {
await checkForDeferredDeepLink()
}
}
private func checkForDeferredDeepLink() async {
do {
let result = try await Dynalinks.checkForDeferredDeepLink()
if result.matched, let link = result.link {
print("Matched! Confidence: \(result.confidence?.rawValue ?? "unknown")")
print("Deep link value: \(link.deepLinkValue ?? "")")
// Parse query params from URL if needed
if let url = link.url,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
print("Query params: \(components.queryItems ?? [])")
}
// Navigate to the deep link destination
handleDeepLink(link)
}
} catch DynalinksError.simulator {
print("Running on simulator - skipped")
} catch {
print("Error: \(error)")
}
}
private func handleDeepLink(_ link: DeepLinkResult.LinkData) {
// Route to appropriate screen based on link.deepLinkValue
// Example: "/product/shoes" -> ProductView(id: "shoes")
}
}For non-async contexts:
Dynalinks.checkForDeferredDeepLink { result in
switch result {
case .success(let deepLink):
if deepLink.matched, let link = deepLink.link {
handleDeepLink(link)
}
case .failure(let error):
print("Error: \(error)")
}
}When your app is opened via a Universal Link (user already has the app installed), use handleUniversalLink to resolve the link and get the full link data:
SwiftUI (with onOpenURL):
@main
struct MyApp: App {
init() {
do {
try Dynalinks.configure(clientAPIKey: "your-client-api-key")
} catch {
print("Failed to configure: \(error)")
}
}
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
Task {
await handleUniversalLink(url)
}
}
}
}
private func handleUniversalLink(_ url: URL) async {
do {
let result = try await Dynalinks.handleUniversalLink(url: url)
if result.matched, let link = result.link {
// Navigate to the deep link destination
print("Deep link: \(link.deepLinkValue ?? "")")
}
} catch {
print("Failed to resolve link: \(error)")
}
}
}UIKit (SceneDelegate):
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard let url = userActivity.webpageURL else { return }
Task {
do {
let result = try await Dynalinks.handleUniversalLink(url: url)
if result.matched, let link = result.link {
// Navigate to destination
}
} catch {
print("Error: \(error)")
}
}
}
}Completion Handler:
Dynalinks.handleUniversalLink(url: url) { result in
switch result {
case .success(let deepLink):
if deepLink.matched, let link = deepLink.link {
handleDeepLink(link)
}
case .failure(let error):
print("Error: \(error)")
}
}Note: When
handleUniversalLinkis called, the SDK automatically skips any subsequent deferred deep link check since the user already has a direct link.
For Universal Links to work, you need to configure Associated Domains in your app. See the iOS Integration Guide for setup instructions.
try Dynalinks.configure(
// Required: Your client API key from the Dynalinks console
clientAPIKey: "your-client-api-key",
// Custom API URL (optional, defaults to production)
baseURL: URL(string: "https://dynalinks.app/api/v1")!,
// Log level (optional)
// .debug - All logs (default in DEBUG builds)
// .info - Info, warnings, and errors
// .warning - Warnings and errors
// .error - Errors only (default in RELEASE builds)
// .none - No logging
logLevel: .debug,
// Allow checks on simulator (optional, defaults to false)
// Useful for development/testing
allowSimulator: true
)When a user clicks a Dynalinks link and your app is already installed:
- User clicks a Dynalinks link → iOS opens your app directly
- App receives URL via
onOpenURLor SceneDelegate - App calls
handleUniversalLink(url:)→ SDK resolves link data - App navigates to deep link destination
When a user clicks a Dynalinks link but needs to install your app first:
- User clicks a Dynalinks link → Opens web preview page
- Web page collects fingerprint → Screen size, OS version, timezone, etc.
- User installs app from App Store
- App calls
checkForDeferredDeepLink()→ SDK collects device fingerprint - Server matches fingerprints → Returns the original link if matched
- App navigates to deep link destination
Note: If
handleUniversalLinkis called first, subsequent calls tocheckForDeferredDeepLinkwill skip the server request since the user already has a direct link.
public struct DeepLinkResult {
/// Whether a matching link was found
public let matched: Bool
/// Confidence level: .high, .medium, or .low
public let confidence: Confidence?
/// Match score (0-100)
public let matchScore: Int?
/// Link data if matched
public let link: LinkData?
}
public struct LinkData {
public let id: String // Link UUID
public let name: String? // Link name
public let path: String? // Path component
public let shortenedPath: String? // Shortened path
public let url: URL? // Original URL (with query params)
public let fullURL: URL? // Full Dynalinks URL
public let deepLinkValue: String? // Deep link value for routing
public let iosDeferredDeepLinkingEnabled: Bool?
// Attribution fields
public let referrer: String? // Referrer tracking parameter
public let providerToken: String? // Apple Search Ads token (pt)
public let campaignToken: String? // Campaign identifier (ct)
// ... additional fields for social sharing, fallback URLs, etc.
}Note: Query parameters are included in the
urlfield. Parse them usingURLComponents:if let url = link.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false) { let params = components.queryItems // [URLQueryItem] }
public enum DynalinksError: Error {
case notConfigured // SDK not configured
case invalidAPIKey(String) // API key is empty or invalid
case simulator // Running on simulator (disabled by default)
case networkError(underlying:) // Network request failed
case invalidResponse // Server returned invalid response
case noMatch // No matching link found
case serverError(statusCode:, message:) // Server returned error status
}- Call early: Check for deferred deep links as early as possible after app launch
- Handle gracefully: The SDK caches results - subsequent calls return cached data
- Don't block UI: Use async/await or completion handlers
- Test on device: Deferred deep linking is disabled on simulator by default
The SDK provides attribution data for campaign tracking and analytics:
let result = try await Dynalinks.checkForDeferredDeepLink()
if result.matched, let link = result.link {
// Track attribution data for analytics
if let referrer = link.referrer {
print("Referrer: \(referrer)") // e.g., "utm_source=facebook&utm_campaign=summer"
}
if let providerToken = link.providerToken {
print("Apple Search Ads token: \(providerToken)") // pt parameter
}
if let campaignToken = link.campaignToken {
print("Campaign: \(campaignToken)") // ct parameter
}
// Send to your analytics platform
Analytics.track("deep_link_opened", properties: [
"referrer": link.referrer,
"provider_token": link.providerToken,
"campaign": link.campaignToken,
"deep_link": link.deepLinkValue
])
}The SDK collects the following device information for fingerprint matching:
- Screen dimensions and scale
- iOS version
- Timezone and language settings
- Device model identifier
- App version and build number
- IDFV (Identifier for Vendor) - No permission required
Note: IDFV is different from IDFA and does not require App Tracking Transparency permission.
You can access the current SDK version programmatically:
print("Dynalinks SDK version: \(Dynalinks.version)") // e.g., "1.0.0"Tests must be run with xcodebuild (not swift test):
xcodebuild test -scheme DynalinksSDK -destination 'platform=iOS Simulator,name=iPhone SE (3rd generation)'MIT License - see LICENSE file for details.