The Dynalinks iOS SDK provides deferred deep linking and Universal Link handling for your iOS app. It automatically matches users to their original link using device fingerprinting, without requiring any user interaction.
Features
- Deferred Deep Linking: Automatically detect which link a user clicked before installing your app
- Universal Link Handling: Resolve link data when your app is opened via Universal Link
- No User Interaction Required: Fingerprint matching happens automatically
- Privacy Friendly: Uses IDFV (no ATT permission required)
Requirements
- iOS 16.0+
- Swift 5.7+
- Xcode 15.0+
Installation
Swift Package Manager
Add the SDK to your project in Xcode:
- Go to File > Add Package Dependencies
- Enter:
https://github.com/dynalinks/dynalinks-ios-sdk - Select version and add to your target
Or add to your Package.swift:
dependencies: [
.package(url: "https://github.com/dynalinks/dynalinks-ios-sdk", from: "1.0.0")
]
Setup
1. Configure the SDK
Initialize the SDK as early as possible in your app’s lifecycle.
SwiftUI:
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()
}
}
}
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
}
}
Get your Client API Key from the Dynalinks Console under your project settings.
2. Configure Associated Domains
For Universal Links to work, add your domain to Signing & Capabilities in Xcode:
- Select your app target
- Go to Signing & Capabilities tab
- Add Associated Domains capability
- Add:
applinks:yourproject.dynalinks.app
See iOS Integration for detailed setup instructions.
Configuration Options
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
)
Usage
Deferred Deep Links
Check for deferred deep links on app launch:
.task {
do {
let result = try await Dynalinks.checkForDeferredDeepLink()
if result.matched, let link = result.link {
// Navigate to link.deepLinkValue
print("Deep link: \(link.deepLinkValue ?? "")")
}
} catch DynalinksError.simulator {
print("Running on simulator - skipped")
} catch {
print("Error: \(error)")
}
}
Completion Handler API (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)")
}
}
Universal Links
Handle Universal Links when your app is opened directly:
SwiftUI:
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
Task {
do {
let result = try await Dynalinks.handleUniversalLink(url: url)
if result.matched, let link = result.link {
// Navigate to link.deepLinkValue
}
} catch {
print("Error: \(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)")
}
}
}
}
DeepLinkResult
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 url: URL? // Original URL (with query params)
public let deepLinkValue: String? // Deep link value for routing
// ... additional fields
}
Query parameters are included in the url field. Parse them using URLComponents:
if let url = link.url,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
let params = components.queryItems // [URLQueryItem]
}
Error Handling
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 serverError(statusCode:, message:) // Server returned error status
}
How It Works
Deferred Deep Links
- User clicks a Dynalinks link (app not installed)
- Web page collects device fingerprint
- User installs app from App Store
- App calls
checkForDeferredDeepLink() - SDK sends device fingerprint to server
- Server matches fingerprints and returns original link
Universal Links
- User clicks a Dynalinks link (app installed)
- iOS opens your app directly via Universal Link
- App calls
handleUniversalLink(url:) - SDK resolves link data from server
Privacy
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)
Note: IDFV is different from IDFA and does not require App Tracking Transparency permission.
More Information
For complete API documentation and additional examples, see the SDK README on GitHub.