StageUp
Mobile SDKDeep Link

iOS

AdStage DeepLink Integration Guide (iOS)

Table of Contents

  1. Overview
  2. Project Setup
  3. Info.plist Configuration
  4. AppDelegate Setup
  5. Handling Deep Link Reception
  6. Creating Deep Links
  7. Advanced Features
  8. Troubleshooting

Overview

The AdStage DeepLink SDK provides the following features:

  • Real-time Deep Links: Immediate processing via URL Scheme and Universal Links
  • Deferred Deep Links: Automatic restoration on first launch after app installation
  • Dynamic Deep Link Creation: Trackable link generation through server API
  • Attribution Tracking: Marketing analysis based on UTM parameters

Project Setup

CocoaPods Configuration

Podfile

platform :ios, '12.0'
 
target 'YourApp' do
  use_frameworks!
  
  # AdStage SDK
  pod 'AdapterAdStage', '~> 3.0'
  
  # Dependencies (automatically installed)
  # Alamofire, SwiftyJSON, etc.
end
 
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
    end
  end
end

Installation:

pod install

Swift Package Manager (SPM)

// Package.swift
dependencies: [
    .package(url: "https://github.com/nbase-io/NBase-SDK-iOS.git", from: "3.0.0")
]

Info.plist Configuration

1. URL Scheme Setup

Add to Info.plist:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>com.example.myapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
    </dict>
</array>

Add to Info.plist:

<key>com.apple.developer.associated-domains</key>
<array>
    <string>applinks:go.myapp.com</string>
    <string>applinks:go.adstage.net</string>
</array>

Xcode Configuration:

  1. Select Signing & Capabilities tab
  2. Click + Capability
  3. Add Associated Domains
  4. Add domains:
    • applinks:go.myapp.com
    • applinks:go.adstage.net

3. Apple App Site Association File

Deploy to server: https://go.myapp.com/.well-known/apple-app-site-association

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAM_ID.com.example.myapp",
        "paths": ["*"]
      }
    ]
  }
}

Important Notes:

  • No file extension (don't add .json)
  • Content-Type: application/json
  • HTTPS required
  • Place in root path or .well-known folder

4. Required Permissions

<!-- Network communication -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
</dict>
 
<!-- Background modes (optional) -->
<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
    <string>remote-notification</string>
</array>

AppDelegate Setup

Swift - AppDelegate.swift

import UIKit
import AdapterAdStage
 
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    // MARK: - Application Lifecycle
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        
        // 1. Initialize AdStage SDK
        initializeAdStage()
        
        // 2. Setup deep link listener
        setupDeepLinkListener()
        
        return true
    }
    
    // MARK: - AdStage Initialization
    
    private func initializeAdStage() {
        AdStageManager.shared.initialize(
            apiKey: "your-api-key-here",
            serverUrl: "https://api.adstage.app"  // Optional
        )
        
        print("✅ AdStage SDK initialized")
    }
    
    private func setupDeepLinkListener() {
        // Setup unified deep link listener
        DeepLinkManager.shared.setUnifiedListener { [weak self] deepLinkData in
            guard let self = self else { return }
            
            print("""
            📱 Deep link received:
            - Short Path: \(deepLinkData.shortPath)
            - Link ID: \(deepLinkData.linkId)
            - Source: \(deepLinkData.source.description)
            - Parameters: \(deepLinkData.parameters)
            """)
            
            // Handle deep link
            self.handleDeepLink(deepLinkData)
        }
    }
    
    // MARK: - Deep Link Handling
    
    private func handleDeepLink(_ data: DeepLinkData) {
        // Handle by source
        switch data.source {
        case .realtime:
            print("🔗 Real-time deep link handling")
            handleRealtimeDeepLink(data)
            
        case .install:
            print("📦 Deferred deep link handling (first launch after install)")
            handleDeferredDeepLink(data)
            
        case .unknown:
            print("❓ Unknown deep link source")
        }
    }
    
    private func handleRealtimeDeepLink(_ data: DeepLinkData) {
        // Navigate based on parameters
        if let campaign = data.parameters["campaign"] {
            navigateToCampaign(campaign)
        } else if let promo = data.parameters["promo"] {
            navigateToPromotion(promo)
        } else {
            navigateToHome()
        }
    }
    
    private func handleDeferredDeepLink(_ data: DeepLinkData) {
        // Handle after onboarding or immediately
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
            self?.handleRealtimeDeepLink(data)
        }
    }
    
    // MARK: - Navigation
    
    private func navigateToCampaign(_ campaign: String) {
        print("🎯 Navigate to campaign: \(campaign)")
        
        DispatchQueue.main.async { [weak self] in
            guard let self = self else { return }
            
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            if let campaignVC = storyboard.instantiateViewController(
                withIdentifier: "CampaignViewController"
            ) as? CampaignViewController {
                campaignVC.campaignId = campaign
                
                if let navController = self.window?.rootViewController as? UINavigationController {
                    navController.pushViewController(campaignVC, animated: true)
                }
            }
        }
    }
    
    private func navigateToPromotion(_ promo: String) {
        print("🎁 Apply promotion: \(promo)")
        // Promotion handling logic
    }
    
    private func navigateToHome() {
        print("🏠 Navigate to home")
        // Home navigation
    }
    
    // MARK: - URL Scheme Deep Link (iOS 8+)
    
    func application(
        _ app: UIApplication,
        open url: URL,
        options: [UIApplication.OpenURLOptionsKey : Any] = [:]
    ) -> Bool {
        
        print("📲 URL Scheme deep link received: \(url)")
        
        // Handle by AdStage
        let handled = DeepLinkManager.shared.handleDeepLink(from: url)
        
        if handled {
            print("✅ AdStage handled the deep link")
            return true
        }
        
        // Handle other URL Schemes (e.g., OAuth callback)
        if url.scheme == "myapp" {
            // Custom handling
            return handleCustomScheme(url)
        }
        
        return false
    }
    
    // MARK: - Universal Links (iOS 9+)
    
    func application(
        _ application: UIApplication,
        continue userActivity: NSUserActivity,
        restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
    ) -> Bool {
        
        // Check if it's a Universal Link
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
              let url = userActivity.webpageURL else {
            return false
        }
        
        print("🌐 Universal Link received: \(url)")
        
        // Handle by AdStage
        let handled = DeepLinkManager.shared.handleUniversalLink(userActivity: userActivity)
        
        if handled {
            print("✅ AdStage handled the Universal Link")
            return true
        }
        
        // Handle other Universal Links
        return handleCustomUniversalLink(url)
    }
    
    // MARK: - Custom Handlers
    
    private func handleCustomScheme(_ url: URL) -> Bool {
        print("🔧 Custom URL Scheme handling: \(url)")
        // OAuth, payment callbacks, etc.
        return false
    }
    
    private func handleCustomUniversalLink(_ url: URL) -> Bool {
        print("🔧 Custom Universal Link handling: \(url)")
        // General web link handling
        return false
    }
}

SwiftUI - App Structure

import SwiftUI
import AdapterAdStage
 
@main
struct MyApp: App {
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    // Handle URL Scheme
                    print("📲 URL received: \(url)")
                    _ = DeepLinkManager.shared.handleDeepLink(from: url)
                }
        }
    }
}
 
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
    ) -> Bool {
        
        // Initialize AdStage
        AdStageManager.shared.initialize(
            apiKey: "your-api-key-here",
            serverUrl: "https://api.adstage.app"
        )
        
        // Setup deep link listener
        setupDeepLinkListener()
        
        return true
    }
    
    private func setupDeepLinkListener() {
        DeepLinkManager.shared.setUnifiedListener { deepLinkData in
            print("📱 Deep link received: \(deepLinkData.shortPath)")
            // Handle logic
        }
    }
    
    func application(
        _ application: UIApplication,
        continue userActivity: NSUserActivity,
        restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
    ) -> Bool {
        // Handle Universal Link
        return DeepLinkManager.shared.handleUniversalLink(userActivity: userActivity)
    }
}

import AdapterAdStage
 
class DeepLinkHandler {
    
    static let shared = DeepLinkHandler()
    
    func setupListener() {
        DeepLinkManager.shared.setUnifiedListener { [weak self] deepLinkData in
            guard let self = self else { return }
            
            // Extract deep link data
            let shortPath = deepLinkData.shortPath
            let parameters = deepLinkData.parameters
            let source = deepLinkData.source
            
            print("""
            ✅ Deep link received successfully
            - Short Path: \(shortPath)
            - Link ID: \(deepLinkData.linkId)
            - Source: \(source.description)
            - Event Type: \(deepLinkData.eventType)
            """)
            
            // Extract UTM parameters
            let utmParams = deepLinkData.getUtmParameters()
            print("📊 UTM Parameters: \(utmParams)")
            
            // Extract custom parameters
            let customParams = deepLinkData.getCustomParameters()
            print("🔧 Custom Parameters: \(customParams)")
            
            // Handle business logic
            self.processDeepLink(deepLinkData)
        }
    }
    
    private func processDeepLink(_ data: DeepLinkData) {
        // Branch by parameter
        if let campaign = data.getParameter("campaign") {
            handleCampaignDeepLink(campaign, data: data)
        } else if let productId = data.getParameter("product_id") {
            handleProductDeepLink(productId, data: data)
        } else if let promo = data.getParameter("promo") {
            handlePromoDeepLink(promo, data: data)
        } else {
            handleDefaultDeepLink(data)
        }
    }
    
    private func handleCampaignDeepLink(_ campaign: String, data: DeepLinkData) {
        print("🎯 Campaign deep link: \(campaign)")
        
        DispatchQueue.main.async {
            // Navigate to campaign screen
            NotificationCenter.default.post(
                name: .navigateToCampaign,
                object: nil,
                userInfo: ["campaign": campaign, "data": data]
            )
        }
    }
    
    private func handleProductDeepLink(_ productId: String, data: DeepLinkData) {
        print("🛍️ Product deep link: \(productId)")
        
        DispatchQueue.main.async {
            NotificationCenter.default.post(
                name: .navigateToProduct,
                object: nil,
                userInfo: ["productId": productId]
            )
        }
    }
    
    private func handlePromoDeepLink(_ promo: String, data: DeepLinkData) {
        print("🎁 Promotion deep link: \(promo)")
        
        // Auto-apply promotion code
        applyPromoCode(promo)
    }
    
    private func handleDefaultDeepLink(_ data: DeepLinkData) {
        print("📋 Default deep link handling")
        
        DispatchQueue.main.async {
            // Navigate to home
            NotificationCenter.default.post(name: .navigateToHome, object: nil)
        }
    }
    
    private func applyPromoCode(_ code: String) {
        // Promotion application logic
        print("Applying promo code: \(code)")
    }
}
 
// Notification name definitions
extension Notification.Name {
    static let navigateToCampaign = Notification.Name("navigateToCampaign")
    static let navigateToProduct = Notification.Name("navigateToProduct")
    static let navigateToHome = Notification.Name("navigateToHome")
}
// Deferred deep links are automatically handled at AdStageManager.initialize()
// No separate implementation required!
 
// Automatically at initialization:
// 1. Collect device fingerprint
// 2. Request matching from server
// 3. Automatically call onDeepLinkReceived if saved deep link exists
 
// Manual handling if needed:
Task {
    await DeferredDeepLinkHandler.shared.handleDeferredDeepLink()
}

3. Handling by Source

func handleDeepLink(_ data: DeepLinkData) {
    switch data.source {
    case .realtime:
        print("🔗 Real-time deep link - received while app running")
        // Can navigate immediately
        navigateImmediately(data)
        
    case .install:
        print("📦 Deferred deep link - first launch after install")
        // Handle after onboarding or delay
        showOnboardingThenNavigate(data)
        
    case .unknown:
        print("❓ Unknown source")
        handleAsDefault(data)
    }
}
 
private func showOnboardingThenNavigate(_ data: DeepLinkData) {
    // Handle after onboarding completion
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in
        self?.navigateImmediately(data)
    }
}

1. Builder Pattern

import AdapterAdStage
 
class DeepLinkCreator {
    
    // Simple deep link creation
    func createSimpleDeepLink() {
        DeepLinkManager.shared.createDeepLink()
            .setName("Summer Promotion")
            .setDescription("2024 Summer Season Promotion")
            .setChannel("google-ads")
            .setCampaign("summer2024")
            .addParameter(key: "promo", value: "SUMMER20")
            .create { [weak self] deepLinkInfo, error in
                if let error = error {
                    print("❌ Deep link creation failed: \(error.localizedDescription)")
                    return
                }
                
                guard let info = deepLinkInfo else {
                    print("❌ No deep link info")
                    return
                }
                
                print("""
                ✅ Deep link created
                - Short URL: \(info.shortUrl)
                - Short Path: \(info.shortPath)
                - ID: \(info.id)
                """)
                
                // Share generated URL
                self?.shareDeepLink(info.shortUrl)
            }
    }
    
    // Full options included
    func createFullDeepLink() {
        DeepLinkManager.shared.createDeepLink()
            // Basic info
            .setName("Winter Promotion")
            .setDescription("2024-2025 Winter Season Promotion")
            .setShortPath("WINTER24")  // Custom path
            
            // Tracking parameters
            .setChannel("facebook-ads")
            .setSubChannel("instagram")
            .setCampaign("winter2024")
            .setAdGroup("fashion-lovers")
            .setCreative("banner-001")
            .setContent("hero-image")
            .setKeyword("winter-sale")
            
            // Android configuration
            .setAndroidConfig(
                packageName: "com.example.myapp",
                appScheme: "myapp://promo/winter",
                webUrl: "https://example.com/promo/winter"
            )
            
            // iOS configuration
            .setIosConfig(
                appStoreId: "123456789",
                appScheme: "myapp://promo/winter",
                webUrl: "https://example.com/promo/winter"
            )
            
            // Web configuration
            .setWebConfig(webUrl: "https://example.com/promo/winter")
            
            // Custom parameters
            .addParameter(key: "discount", value: "30")
            .addParameter(key: "promoCode", value: "WINTER30")
            .addParameter(key: "validUntil", value: "2025-03-31")
            
            .create { deepLinkInfo, error in
                if let error = error {
                    print("❌ Error: \(error.localizedDescription)")
                    return
                }
                
                guard let info = deepLinkInfo else { return }
                print("✅ Short URL: \(info.shortUrl)")
            }
    }
    
    // Set multiple parameters at once
    func createDeepLinkWithMultipleParams() {
        let parameters: [String: String] = [
            "campaign": "spring2024",
            "discount": "15",
            "promoCode": "SPRING15",
            "source": "email"
        ]
        
        DeepLinkManager.shared.createDeepLink()
            .setName("Spring Promotion")
            .setChannel("email-marketing")
            .setParameters(parameters)
            .create { deepLinkInfo, error in
                // Handle
            }
    }
    
    // Share functionality
    private func shareDeepLink(_ url: String) {
        DispatchQueue.main.async {
            let text = """
            🎁 Special Promotion Invite!
            
            Sign up through this link and get a $50 discount coupon.
            \(url)
            """
            
            let activityVC = UIActivityViewController(
                activityItems: [text],
                applicationActivities: nil
            )
            
            if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
               let rootVC = windowScene.windows.first?.rootViewController {
                rootVC.present(activityVC, animated: true)
            }
        }
    }
}

2. async/await Approach

class AsyncDeepLinkCreator {
    
    func createDeepLink() async {
        do {
            let builder = DeepLinkManager.shared.createDeepLink()
                .setName("New User Event")
                .setChannel("referral")
                .setCampaign("invite-friend")
                .addParameter(key: "referrer", value: "USER123")
                .addParameter(key: "bonus", value: "5000")
            
            let info = try await builder.createAsync()
            
            print("✅ Deep link created: \(info.shortUrl)")
            
            // Update UI
            await MainActor.run {
                self.updateUI(with: info.shortUrl)
            }
            
        } catch {
            print("❌ Deep link creation failed: \(error.localizedDescription)")
            
            await MainActor.run {
                self.showError(error)
            }
        }
    }
    
    @MainActor
    private func updateUI(with url: String) {
        // Update UI
    }
    
    @MainActor
    private func showError(_ error: Error) {
        // Show error
    }
}

3. Objective-C Compatible Approach

// Simple API available from Objective-C
class ObjCCompatibleDeepLinkCreator {
    
    @objc func createSimpleDeepLink(name: String, completion: @escaping (String?, Error?) -> Void) {
        AdStageSDK.shared.createSimpleDeepLink(name: name, completion: completion)
    }
    
    @objc func createDeepLinkWithDescription(
        name: String,
        description: String,
        completion: @escaping (String?, Error?) -> Void
    ) {
        AdStageSDK.shared.createSimpleDeepLink(
            name: name,
            description: description,
            completion: completion
        )
    }
}

Advanced Features

1. Global User Attributes Setup

// On login
func onUserLogin(userId: String, userProfile: UserProfile) {
    AdStageManager.shared.setUserId(userId)
    
    let userAttributes = UserAttributes(
        gender: userProfile.gender,
        country: userProfile.country,
        city: userProfile.city,
        age: "\(userProfile.age)",
        language: Locale.current.languageCode ?? "en"
    )
    AdStageManager.shared.setUserAttributes(userAttributes)
    
    print("✅ User info setup complete")
}
 
// On logout
func onUserLogout() {
    AdStageManager.shared.setUserId(nil)
    AdStageManager.shared.setUserAttributes(nil)
    
    print("🚪 User info cleared")
}

2. Global Device Info Setup

import UIKit
 
func setupDeviceInfo() {
    let device = UIDevice.current
    let app = Bundle.main
    
    let deviceInfo = DeviceInfo(
        category: device.userInterfaceIdiom == .pad ? "tablet" : "mobile",
        platform: "iOS",
        model: device.model,
        appVersion: app.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown",
        osVersion: device.systemVersion
    )
    
    AdStageManager.shared.setDeviceInfo(deviceInfo)
    
    print("""
    📱 Device info setup:
    - Category: \(deviceInfo.category)
    - Platform: \(deviceInfo.platform)
    - Model: \(deviceInfo.model)
    - App Version: \(deviceInfo.appVersion)
    - OS Version: \(deviceInfo.osVersion)
    """)
}

3. Promotion Banner Integration

import AdapterAdStage
 
class PromotionManager {
    
    // Fetch promotion list
    func fetchPromotions() async {
        let params = AdStageSDK.PromotionParams()
        params.bannerType = "NATIVE"
        params.region = "KR"
        params.limit = NSNumber(value: 10)
        
        if let response = await AdStageSDK.shared.getPromotionList(params: params) {
            print("✅ Fetched \(response.promotions.count) promotions")
            
            response.promotions.forEach { promotion in
                print("""
                - \(promotion.appName)
                  Banner: \(promotion.bannerUrl)
                  CPI: \(promotion.cpi)
                """)
            }
        }
    }
    
    // Open promotion banner
    func openPromotionBanner() {
        let params = AdStageSDK.PromotionParams()
        params.bannerType = "INTERSTITIAL"
        params.region = "KR"
        
        AdStageSDK.shared.openPromotion(
            params: params,
            showTodayButton: true
        ) { url, error in
            if let error = error {
                print("❌ Failed to open promotion: \(error.localizedDescription)")
                return
            }
            
            if let url = url {
                print("✅ Promotion URL: \(url)")
            }
        }
    }
}

Troubleshooting

Checklist:

  • Verify Associated Domains setup
  • Verify apple-app-site-association file deployment
  • Verify HTTPS usage
  • Verify Team ID and Bundle ID match

Verification Method:

# 1. Check file accessibility
curl https://go.myapp.com/.well-known/apple-app-site-association
 
# 2. Check Apple CDN cache
curl https://app-site-association.cdn-apple.com/a/v1/go.myapp.com

Debugging:

func application(
    _ application: UIApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
    
    print("""
    Universal Link Debug:
    - Activity Type: \(userActivity.activityType)
    - Webpage URL: \(userActivity.webpageURL?.absoluteString ?? "nil")
    - User Info: \(userActivity.userInfo ?? [:])
    """)
    
    return DeepLinkManager.shared.handleUniversalLink(userActivity: userActivity)
}

2. URL Scheme Not Working

Check:

func application(
    _ app: UIApplication,
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
    
    print("""
    URL Scheme Debug:
    - URL: \(url)
    - Scheme: \(url.scheme ?? "nil")
    - Host: \(url.host ?? "nil")
    - Path: \(url.path)
    - Query: \(url.query ?? "nil")
    - Source App: \(options[.sourceApplication] ?? "nil")
    """)
    
    return DeepLinkManager.shared.handleDeepLink(from: url)
}

Causes:

  • Device fingerprint collection failed
  • Server matching failed
  • Network connection issue

Check:

// Manually handle deferred deep link
Task {
    await DeferredDeepLinkHandler.shared.handleDeferredDeepLink()
}
 
// Check logs
print("✅ Starting deferred deep link handling")

SceneDelegate Setup (iOS 13+):

import UIKit
import AdapterAdStage
 
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
    var window: UIWindow?
    
    func scene(
        _ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions
    ) {
        guard let _ = (scene as? UIWindowScene) else { return }
        
        // Handle URL Context
        if let urlContext = connectionOptions.urlContexts.first {
            _ = DeepLinkManager.shared.handleDeepLink(from: urlContext.url)
        }
        
        // Handle User Activity
        if let userActivity = connectionOptions.userActivities.first {
            _ = DeepLinkManager.shared.handleUniversalLink(userActivity: userActivity)
        }
    }
    
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        if let url = URLContexts.first?.url {
            _ = DeepLinkManager.shared.handleDeepLink(from: url)
        }
    }
    
    func scene(
        _ scene: UIScene,
        continue userActivity: NSUserActivity
    ) {
        _ = DeepLinkManager.shared.handleUniversalLink(userActivity: userActivity)
    }
}

5. Build Settings Check

Build Settings:

- Deployment Target: iOS 12.0 or higher
- Swift Language Version: 5.0 or higher
- Enable Bitcode: No (removed in Xcode 14+)

Required Info.plist Items:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>myapp</string>
</array>

Testing Methods

1. URL Scheme Testing

Testing in Safari:

myapp://promo/summer
myapp://promo?campaign=summer&discount=20

Testing in Terminal:

xcrun simctl openurl booted "myapp://promo/summer"

Testing in Safari:

https://go.myapp.com/ABCDEF

Testing in Notes App:

  1. Enter link in Notes app
  2. Long press the link
  3. Select "Open in [app name]"

Testing in Terminal:

xcrun simctl openurl booted "https://go.myapp.com/ABCDEF"

3. Log Verification

// Verify deep link reception
DeepLinkManager.shared.setUnifiedListener { deepLinkData in
    print("""
    ==========================================
    Deep Link Reception Test
    ==========================================
    Short Path: \(deepLinkData.shortPath)
    Link ID: \(deepLinkData.linkId)
    Source: \(deepLinkData.source.description)
    Parameters:
    \(deepLinkData.parameters.map { "  - \($0.key): \($0.value)" }.joined(separator: "\n"))
    ==========================================
    """)
}

References


Table of Contents