A lightweight iOS companion library for the Traceback Firebase extension - a modern replacement for Firebase Dynamic Links.
Install the firebase extension into your firebase project. https://github.com/InQBarna/firebase-traceback-extension
In your iOS app:
- Install this companion sdk using SPM
https://github.com/InQBarna/traceback-iOS- In the Info tab of your app's Xcode project, create a new URL type to be used for Traceback. Set the Identifier field to a unique value and the URL scheme field to be your bundle identifier.
- In the Capabilities tab of your app's Xcode project, enable Associated Domains and add the following to the Associated Domains list:
applinks:your_dynamic_links_domain
Create your configuration and grab the sdk tool instance
import Traceback
/* ... */
lazy var traceback: TracebackSDK = {
let config = TracebackConfiguration(
mainAssociatedHost: URL(string: "https://your-project-traceback.firebaseapp.com")!,
useClipboard: false,
logLevel: .error
)
return TracebackSDK.live(config: config)
}()The TracebackConfiguration supports these parameters:
mainAssociatedHost: (required) The main domain created by the Traceback Firebase extensionassociatedHosts: (optional) Additional domains associated with your appuseClipboard: (default: true) Enable clipboard reading for better post-install detectionlogLevel: (default: .info) Logging verbosity (.error,.debug,.info)
let config = TracebackConfiguration(
mainAssociatedHost: URL(string: "https://your-project-traceback.firebaseapp.com")!,
associatedHosts: [
URL(string: "https://your-custom-domain.com")!
],
useClipboard: false,
logLevel: .debug
)We recommend grabbing and handling links within your root SwiftUI view. Choose the right root view, where you can handle deep navigations. See the example implementation below
struct PreLandingView: View {
@ObservedObject var viewModel: PreLandingViewModel
var body: some View {
/* ... */
.onAppear {
Task {
do {
// 1.- Search for post-install link and proceed if available
let result = try await traceback.postInstallSearchLink()
if let tracebackURL = result.url {
proceed(onOpenURL: tracebackURL)
}
} catch {
// Handle error - network issues, configuration problems, etc.
logger.error("Failed to search for post-install link: \(error)")
}
}
}
.onOpenURL { url in
proceed(onOpenURL: url)
}
}
// This method is to be called from onOpenURL or after post install link search
func proceed(onOpenURL url: URL) {
// 2.- Check if this is a Traceback URL
guard traceback.isTracebackURL(url) else {
// Not a Traceback URL, handle it elsewhere
handleDeepLink(linkURL)
return
}
Task {
do {
// 3.- Check if dynamic campaign link exists (resolves the deep link from the URL)
let linkResult = try await traceback.campaignSearchLink(url)
guard let linkURL = linkResult.url else {
// No deep link found in this URL, so we normally continue opening the app Landing screen
return
}
// 4.- Handle the url, opening the right content indicated by linkURL
// Use linkURL to navigate to the appropriate content in your app
// You can also access linkResult.analytics for tracking purposes
handleDeepLink(linkURL)
sendAnalytics(linkResult.analytics)
} catch {
// Handle error - network issues, invalid URL, etc.
logger.error("Failed to resolve campaign link: \(error)")
}
}
}
}@MainActor
class YourAppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
Task {
do {
// 1.- Trigger a search for installation links
// if a link is found successfully, it will be sent to proceed(openURL:) below
let result = try await traceback.postInstallSearchLink()
if let tracebackURL = result.url {
proceed(onOpenURL: tracebackURL)
}
} catch {
// Handle error - network issues, configuration problems, etc.
logger.error("Failed to search for post-install link: \(error)")
}
}
return true
}
/* ... */
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any]
) -> Bool {
proceed(onOpenURL: url)
return true
}
// This method is to be called from application(open:options:) or after post install link search
func proceed(onOpenURL url: URL) {
// 2.- Check if this is a Traceback URL
guard traceback.isTracebackURL(url) else {
// Not a Traceback URL, handle it elsewhere
handleDeepLink(linkURL)
return
}
Task {
do {
// 3.- Check if dynamic campaign link exists (resolves the deep link from the URL)
let linkResult = try await traceback.campaignSearchLink(url)
guard let linkURL = linkResult.url else {
// No deep link found in this URL, so we normally continue opening the app Landing screen
return
}
// 4.- Handle the url, opening the right content indicated by linkURL
// Use linkURL to navigate to the appropriate content in your app
// You can also access linkResult.analytics for tracking purposes
handleDeepLink(linkURL)
sendAnalytics(linkResult.analytics)
} catch {
// Handle error - network issues, invalid URL, etc.
logger.error("Failed to resolve campaign link: \(error)")
}
}
}
}The SDK includes a comprehensive diagnostics tool to validate your dynamic links configuration:
// Call this during app development to verify your setup
traceback.performDiagnostics()This will output detailed diagnostic information using os_log (viewable in Console.app or Xcode debug console), including:
- SDK version and app information
- Configuration host validation (HTTPS, valid hostname)
- Additional hosts validation (if configured)
- Clipboard settings with recommendations
- URL scheme configuration in Info.plist
- UIApplication delegate method implementation
- Associated Domains entitlements validation
- Universal Links setup verification
- iOS Simulator warnings (Universal Links don't work in simulator)
- Network connectivity guidance
- Production readiness recommendations
The diagnostics will categorize issues as:
- ❌ Errors: Must be fixed before production use
⚠️ Warnings: Should be addressed for optimal functionality- ✅ Success: Configuration is correct
Searches for the deep link that triggered the app installation. Call this once during app launch.
Resolves a Traceback URL opened via Universal Link or custom URL scheme into a deep link.
Validates if the given URL matches any of the configured Traceback domains.
Runs comprehensive validation of your Traceback configuration and outputs diagnostic information.
The result object returned by postInstallSearchLink() and campaignSearchLink() contains:
url: URL?- The extracted deep link URL to navigate tomatchType: MatchType- How the link was detected (.unique,.heuristics,.ambiguous,.intent,.none,.unknown)analytics: [TracebackAnalyticsEvent]- Analytics events you can send to your preferred platform
Configuration object for initializing the SDK:
public struct TracebackConfiguration {
public let mainAssociatedHost: URL
public let associatedHosts: [URL]?
public let useClipboard: Bool
public let logLevel: LogLevel
public enum LogLevel: Int {
case error // Only errors
case debug // Debug info, warnings, and errors
case info // Verbose logging (all messages)
}
}The SDK uses Swift's error handling mechanisms. Both postInstallSearchLink() and campaignSearchLink() can throw errors:
do {
let result = try await traceback.postInstallSearchLink()
if let url = result.url {
// Handle successful link detection
handleDeepLink(url)
// Send analytics events
sendAnalytics(result.analytics)
} else {
// No link found - normal app startup
handleNormalStartup()
}
} catch {
// Handle network or configuration errors
logger.error("Failed to search for post-install link: \(error)")
handleNormalStartup()
}do {
let result = try await traceback.campaignSearchLink(url)
if let deepLink = result.url {
// Handle successful link resolution
handleDeepLink(deepLink)
// Send analytics events
sendAnalytics(result.analytics)
} else {
// URL is valid Traceback URL but no deep link found
handleNormalStartup()
}
} catch {
// Handle network or configuration errors
logger.error("Failed to resolve campaign link: \(error)")
}- ✅ Verify
useClipboard: truein configuration - ✅ Test on physical device (simulator limitations)
- ✅ Check Universal Links setup with diagnostics
- ✅ Ensure associated domains are properly configured
- ✅ Run
traceback.performDiagnostics()to validate setup - ✅ Verify Associated Domains in app entitlements
- ✅ Check URL scheme configuration in Info.plist
- ✅ Test with physical device (not simulator)
- ✅ Verify
mainAssociatedHostURL is accessible - ✅ Check firewall/network restrictions
- ✅ Test with different network connection
- Use Diagnostics: Always run diagnostics during development
- Check Console: Monitor os_log output for detailed information
- Test Scenarios: Test both post-install and direct link scenarios
- Analytics: Use the analytics events for debugging link detection
- Always use HTTPS URLs for associated hosts
- Validate deep link URLs before navigation
- Don't log sensitive information
- Call
postInstallSearchLink()only once per app launch - Handle errors gracefully to avoid blocking app startup
- Use analytics events for monitoring and debugging
- Test on physical devices for Universal Links validation
- Use different network conditions
- Test both fresh installs and existing app scenarios
- Verify clipboard functionality works as expected