adstage
移动 SDK深度链接

iOS

AdStage DeepLink 集成指南(iOS)

目录

  1. 概览
  2. 项目设置
  3. Info.plist 设置
  4. AppDelegate 设置
  5. 深度链接接收处理
  6. 创建深度链接
  7. 高级功能
  8. 问题排查

概览

AdStage DeepLink SDK 提供以下功能:

  • 实时深度链接:通过 URL Scheme、Universal Links 即时处理
  • 延迟深度链接:应用安装后首次启动时自动还原
  • 动态深度链接创建:通过服务器 API 创建可跟踪的链接
  • 归因跟踪:基于 UTM 参数的营销分析

项目设置

CocoaPods 设置

Podfile

platform :ios, '15.0'
 
target 'YourApp' do
  use_frameworks!
  
  # AdStage SDK
  pod 'AdapterAdStage', '3.0.11'
end
 
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
    end
  end
end

安装:

pod install

Info.plist 设置

1. URL Scheme 设置

在 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>

在 Info.plist 中添加:

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

Xcode 设置:

  1. 选择 Signing & Capabilities 标签页
  2. 点击 + Capability
  3. 添加 Associated Domains
  4. 添加域名:
    • applinks:go.myapp.com
    • applinks:go.adstage.net

3. Apple App Site Association 文件

部署到服务器:https://go.myapp.com/.well-known/apple-app-site-association

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

注意事项:

  • 没有文件扩展名(不要加 .json)
  • Content-Type:application/json
  • 必须使用 HTTPS
  • 放置于根路径或 .well-known 文件夹

4. 必需权限设置

<!-- 网络通信 -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
</dict>
 
<!-- 后台模式(可选) -->
<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
    <string>remote-notification</string>
</array>

AppDelegate 设置

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. 初始化 AdStage SDK
        initializeAdStage()
        
        // 2. 设置深度链接监听器
        setupDeepLinkListener()
        
        return true
    }
    
    // MARK: - AdStage Initialization
    
    private func initializeAdStage() {
        AdStageManager.shared.initialize(
            apiKey: "your-api-key-here",
            serverUrl: "https://api.adstage.app"  // 可选
        )
        
        print("✅ AdStage SDK 초기화 완료")
    }
    
    private func setupDeepLinkListener() {
        // 设置统一的深度链接监听器
        DeepLinkManager.shared.setDeepLinkListener { [weak self] deepLinkData in
            guard let self = self else { return }
            
            print("""
            📱 딥링크 수신:
            - Short Path: \(deepLinkData.shortPath)
            - Link ID: \(deepLinkData.linkId)
            - Source: \(deepLinkData.source.description)
            - Parameters: \(deepLinkData.parameters)
            """)
            
            // 处理深度链接
            self.handleDeepLink(deepLinkData)
        }
    }
    
    // MARK: - Deep Link Handling
    
    private func handleDeepLink(_ data: DeepLinkData) {
        // 按来源分别处理
        switch data.source {
        case .realtime:
            print("🔗 실시간 딥링크 처리")
            handleRealtimeDeepLink(data)
            
        case .install:
            print("📦 디퍼드 딥링크 처리 (앱 설치 후 첫 실행)")
            handleDeferredDeepLink(data)
            
        case .unknown:
            print("❓ 알 수 없는 딥링크 소스")
        }
    }
    
    private func handleRealtimeDeepLink(_ data: DeepLinkData) {
        // 基于参数的页面跳转
        if let campaign = data.parameters["campaign"] {
            navigateToCampaign(campaign)
        } else if let promo = data.parameters["promo"] {
            navigateToPromotion(promo)
        } else {
            navigateToHome()
        }
    }
    
    private func handleDeferredDeepLink(_ data: DeepLinkData) {
        // 在引导流程之后处理,或立即处理
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
            self?.handleRealtimeDeepLink(data)
        }
    }
    
    // MARK: - Navigation
    
    private func navigateToCampaign(_ campaign: String) {
        print("🎯 캠페인 화면으로 이동: \(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("🎁 프로모션 적용: \(promo)")
        // 推广处理逻辑
    }
    
    private func navigateToHome() {
        print("🏠 홈 화면으로 이동")
        // 跳转到首页
    }
    
    // MARK: - URL Scheme Deep Link (iOS 8+)
    
    func application(
        _ app: UIApplication,
        open url: URL,
        options: [UIApplication.OpenURLOptionsKey : Any] = [:]
    ) -> Bool {
        
        print("📲 URL Scheme 딥링크 수신: \(url)")
        
        // 交由 AdStage 处理
        let handled = DeepLinkManager.shared.handleDeepLink(url)
        
        if handled {
            print("✅ AdStage가 딥링크를 처리했습니다")
            return true
        }
        
        // 处理其他 URL Scheme(例如 OAuth 回调)
        if url.scheme == "myapp" {
            // 自定义处理
            return handleCustomScheme(url)
        }
        
        return false
    }
    
    // MARK: - Universal Links (iOS 9+)
    
    func application(
        _ application: UIApplication,
        continue userActivity: NSUserActivity,
        restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
    ) -> Bool {
        
        // 确认是否为 Universal Link
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
              let url = userActivity.webpageURL else {
            return false
        }
        
        print("🌐 Universal Link 수신: \(url)")
        
        // 交由 AdStage 处理
        let handled = DeepLinkManager.shared.handleUniversalLink(userActivity)
        
        if handled {
            print("✅ AdStage가 Universal Link를 처리했습니다")
            return true
        }
        
        // 处理其他 Universal Link
        return handleCustomUniversalLink(url)
    }
    
    // MARK: - Custom Handlers
    
    private func handleCustomScheme(_ url: URL) -> Bool {
        print("🔧 커스텀 URL Scheme 처리: \(url)")
        // 处理 OAuth、支付回调等
        return false
    }
    
    private func handleCustomUniversalLink(_ url: URL) -> Bool {
        print("🔧 커스텀 Universal Link 처리: \(url)")
        // 处理普通网页链接
        return false
    }
}

SwiftUI - App 结构

import SwiftUI
import AdapterAdStage
 
@main
struct MyApp: App {
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    // 处理 URL Scheme
                    print("📲 URL 수신: \(url)")
                    _ = DeepLinkManager.shared.handleDeepLink(url)
                }
        }
    }
}
 
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
    ) -> Bool {
        
        // 初始化 AdStage
        AdStageManager.shared.initialize(
            apiKey: "your-api-key-here",
            serverUrl: "https://api.adstage.app"
        )
        
        // 设置深度链接监听器
        setupDeepLinkListener()
        
        return true
    }
    
    private func setupDeepLinkListener() {
        DeepLinkManager.shared.setDeepLinkListener { deepLinkData in
            print("📱 딥링크 수신: \(deepLinkData.shortPath)")
            // 处理逻辑
        }
    }
    
    func application(
        _ application: UIApplication,
        continue userActivity: NSUserActivity,
        restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
    ) -> Bool {
        // 处理 Universal Link
        return DeepLinkManager.shared.handleUniversalLink(userActivity)
    }
}

深度链接接收处理

1. 实时深度链接处理

import AdapterAdStage
 
class DeepLinkHandler {
    
    static let shared = DeepLinkHandler()
    
    func setupListener() {
        DeepLinkManager.shared.setDeepLinkListener { [weak self] deepLinkData in
            guard let self = self else { return }
            
            // 提取深度链接数据
            let shortPath = deepLinkData.shortPath
            let parameters = deepLinkData.parameters
            let source = deepLinkData.source
            
            print("""
            ✅ 딥링크 수신 완료
            - Short Path: \(shortPath)
            - Link ID: \(deepLinkData.linkId)
            - Source: \(source.description)
            - Event Type: \(deepLinkData.eventType)
            """)
            
            // 提取 UTM 参数
            let utmParams = deepLinkData.getUtmParameters()
            print("📊 UTM Parameters: \(utmParams)")
            
            // 提取自定义参数
            let customParams = deepLinkData.getCustomParameters()
            print("🔧 Custom Parameters: \(customParams)")
            
            // 处理业务逻辑
            self.processDeepLink(deepLinkData)
        }
    }
    
    private func processDeepLink(_ data: DeepLinkData) {
        // 按参数分支处理
        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)")
        
        DispatchQueue.main.async {
            // 跳转到广告系列页面
            NotificationCenter.default.post(
                name: .navigateToCampaign,
                object: nil,
                userInfo: ["campaign": campaign, "data": data]
            )
        }
    }
    
    private func handleProductDeepLink(_ productId: String, data: DeepLinkData) {
        print("🛍️ 상품 딥링크: \(productId)")
        
        DispatchQueue.main.async {
            NotificationCenter.default.post(
                name: .navigateToProduct,
                object: nil,
                userInfo: ["productId": productId]
            )
        }
    }
    
    private func handlePromoDeepLink(_ promo: String, data: DeepLinkData) {
        print("🎁 프로모션 딥링크: \(promo)")
        
        // 自动应用推广码
        applyPromoCode(promo)
    }
    
    private func handleDefaultDeepLink(_ data: DeepLinkData) {
        print("📋 기본 딥링크 처리")
        
        DispatchQueue.main.async {
            // 跳转到首页
            NotificationCenter.default.post(name: .navigateToHome, object: nil)
        }
    }
    
    private func applyPromoCode(_ code: String) {
        // 推广应用逻辑
        print("프로모션 코드 적용: \(code)")
    }
}
 
// 定义 Notification 名称
extension Notification.Name {
    static let navigateToCampaign = Notification.Name("navigateToCampaign")
    static let navigateToProduct = Notification.Name("navigateToProduct")
    static let navigateToHome = Notification.Name("navigateToHome")
}

2. 延迟深度链接(自动处理)

// 延迟深度链接会在 AdStageManager.initialize() 时自动处理。
// 无需另行实现!
 
// 初始化时会自动:
// 1. 收集设备指纹
// 2. 向服务器发起匹配请求
// 3. 若存在已保存的深度链接,则自动调用 onDeepLinkReceived
 
// 延迟深度链接不提供手动触发 API,在 initialize() 之后自动处理。
// 匹配结果会通过上面注册的 setDeepLinkListener 回调传递。

3. 按来源处理

func handleDeepLink(_ data: DeepLinkData) {
    switch data.source {
    case .realtime:
        print("🔗 실시간 딥링크 - 앱 실행 중 수신")
        // 可立即切换页面
        navigateImmediately(data)
        
    case .install:
        print("📦 디퍼드 딥링크 - 앱 설치 후 첫 실행")
        // 在引导流程之后处理,或延迟处理
        showOnboardingThenNavigate(data)
        
    case .unknown:
        print("❓ 알 수 없는 소스")
        handleAsDefault(data)
    }
}
 
private func showOnboardingThenNavigate(_ data: DeepLinkData) {
    // 引导流程完成后处理
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in
        self?.navigateImmediately(data)
    }
}

创建深度链接

1. Builder 模式

import AdapterAdStage
 
class DeepLinkCreator {
    
    // 创建简单的深度链接
    func createSimpleDeepLink() {
        AdStageManager.shared.createDeepLinkBuilder()
            .setName("여름 프로모션")
            .setDescription("2024년 여름 시즌 프로모션")
            .setChannel("google-ads")
            .setCampaign("summer2024")
            .addParameter(key: "promo", value: "SUMMER20")
            .create { [weak self] deepLinkInfo, error in
                if let error = error {
                    print("❌ 딥링크 생성 실패: \(error.localizedDescription)")
                    return
                }
                
                guard let info = deepLinkInfo else {
                    print("❌ 딥링크 정보 없음")
                    return
                }
                
                print("""
                ✅ 딥링크 생성 완료
                - Short URL: \(info.shortUrl)
                - Short Path: \(info.shortPath)
                - Link ID: \(info.linkId)
                """)
                
                // 分享生成的 URL
                self?.shareDeepLink(info.shortUrl)
            }
    }
    
    // 包含全部选项
    func createFullDeepLink() {
        AdStageManager.shared.createDeepLinkBuilder()
            // 基本信息
            .setName("겨울 프로모션")
            .setDescription("2024-2025 겨울 시즌 프로모션")
            .setShortPath("WINTER24")  // 用户指定的路径
            
            // 跟踪参数
            .setChannel("facebook-ads")
            .setSubChannel("instagram")
            .setCampaign("winter2024")
            .setAdGroup("fashion-lovers")
            .setCreative("banner-001")
            .setContent("hero-image")
            .setKeyword("winter-sale")
            
            // Android 设置
            .setAndroidConfig(
                packageName: "com.example.myapp",
                appScheme: "myapp://promo/winter",
                webUrl: "https://example.com/promo/winter"
            )
            
            // iOS 设置
            .setIosConfig(
                appStoreId: "123456789",
                appScheme: "myapp://promo/winter",
                webUrl: "https://example.com/promo/winter"
            )
            
            // 网页设置
            .setWebConfig(webUrl: "https://example.com/promo/winter")
            
            // 自定义参数
            .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.localizedDescription)")
                    return
                }
                
                guard let info = deepLinkInfo else { return }
                print("✅ Short URL: \(info.shortUrl)")
            }
    }
    
    // 一次性设置多个参数
    func createDeepLinkWithMultipleParams() {
        let parameters: [String: String] = [
            "campaign": "spring2024",
            "discount": "15",
            "promoCode": "SPRING15",
            "source": "email"
        ]
        
        AdStageManager.shared.createDeepLinkBuilder()
            .setName("봄 프로모션")
            .setChannel("email-marketing")
            .setParameters(parameters)
            .create { deepLinkInfo, error in
                // 处理
            }
    }
    
    // 分享功能
    private func shareDeepLink(_ url: String) {
        DispatchQueue.main.async {
            let text = """
            🎁 특별 프로모션 초대!
            
            이 링크를 통해 가입하면 5,000원 할인 쿠폰을 드립니다.
            \(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 方式

class AsyncDeepLinkCreator {
    
    func createDeepLink() async {
        do {
            let builder = AdStageManager.shared.createDeepLinkBuilder()
                .setName("신규 가입 이벤트")
                .setChannel("referral")
                .setCampaign("invite-friend")
                .addParameter(key: "referrer", value: "USER123")
                .addParameter(key: "bonus", value: "5000")
            
            let info = try await builder.createAsync()
            
            print("✅ 딥링크 생성 완료: \(info.shortUrl)")
            
            // 更新 UI
            await MainActor.run {
                self.updateUI(with: info.shortUrl)
            }
            
        } catch {
            print("❌ 딥링크 생성 실패: \(error.localizedDescription)")
            
            await MainActor.run {
                self.showError(error)
            }
        }
    }
    
    @MainActor
    private func updateUI(with url: String) {
        // 更新 UI
    }
    
    @MainActor
    private func showError(_ error: Error) {
        // 显示错误
    }
}

3. Objective-C 兼容方式

createDeepLinkBuilder() 与 DeepLinkBuilder 的链式方法、create(completion:) 均已通过 @objc 暴露,因此在 Objective-C 中也可直接使用。 你可以编写一个简单的包装器,让回调只返回 Short URL 字符串。

// 可在 Objective-C 中使用的简单包装器
@objc class ObjCCompatibleDeepLinkCreator: NSObject {
    
    @objc func createSimpleDeepLink(name: String, completion: @escaping (String?, Error?) -> Void) {
        AdStageManager.shared.createDeepLinkBuilder()
            .setName(name)
            .create { info, error in
                completion(info?.shortUrl, error)
            }
    }
    
    @objc func createDeepLinkWithDescription(
        name: String,
        description: String,
        completion: @escaping (String?, Error?) -> Void
    ) {
        AdStageManager.shared.createDeepLinkBuilder()
            .setName(name)
            .setDescription(description)
            .create { info, error in
                completion(info?.shortUrl, error)
            }
    }
}

高级功能

1. 设置全局用户属性

使用 UserAttributes 设置用户信息后,之后发送的事件会自动包含这些信息。 setUserAttributes(_:) 接收一个 UserAttributes 对象作为参数,所有字段均为可选值。

// 登录时
func onUserLogin(userProfile: UserProfile) {
    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("✅ 사용자 정보 설정 완료")
}
 
// 退出登录时(用空属性重置)
func onUserLogout() {
    AdStageManager.shared.setUserAttributes(UserAttributes())
    
    print("🚪 사용자 정보 초기화")
}

参考:设备信息(平台、机型、应用版本、OS 版本、IDFV/IDFA、ATT 状态等) 由 SDK 在事件发送时自动收集并包含,因此无需另行设置。


问题排查

检查清单:

  • 确认 Associated Domains 设置
  • 确认已部署 apple-app-site-association 文件
  • 确认使用 HTTPS
  • 确认 Team ID 与 Bundle ID 一致

验证方法:

# 1. 确认文件是否可访问
curl https://go.myapp.com/.well-known/apple-app-site-association
 
# 2. 确认 Apple CDN 缓存
curl https://app-site-association.cdn-apple.com/a/v1/go.myapp.com

调试:

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)
}

2. URL Scheme 不生效

确认事项:

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(url)
}

3. 延迟深度链接不生效

原因:

  • 设备指纹收集失败
  • 服务器匹配失败
  • 网络连接问题

确认:

// 延迟深度链接在 initialize() 之后自动处理(无手动触发 API)。
// 匹配结果会通过 setDeepLinkListener 回调传递,请在监听器中确认日志。
AdStageManager.shared.setDeepLinkListener { data in
    print("✅ 디퍼드 딥링크 수신: \(data)")
}

4. 应用处于后台时收不到深度链接

SceneDelegate 设置(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 }
        
        // 处理 URL Context
        if let urlContext = connectionOptions.urlContexts.first {
            _ = DeepLinkManager.shared.handleDeepLink(urlContext.url)
        }
        
        // 处理 User Activity
        if let userActivity = connectionOptions.userActivities.first {
            _ = DeepLinkManager.shared.handleUniversalLink(userActivity)
        }
    }
    
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        if let url = URLContexts.first?.url {
            _ = DeepLinkManager.shared.handleDeepLink(url)
        }
    }
    
    func scene(
        _ scene: UIScene,
        continue userActivity: NSUserActivity
    ) {
        _ = DeepLinkManager.shared.handleUniversalLink(userActivity)
    }
}

5. 确认构建设置

Build Settings:

- Deployment Target: iOS 15.0 以上
- Swift Language Version: 5.0 以上
- Enable Bitcode: No (Xcode 14+ 已移除)

Info.plist 必需项:

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

测试方法

1. URL Scheme 测试

在 Safari 中测试:

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

在终端中测试:

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

在 Safari 中测试:

https://go.myapp.com/ABCDEF

在「备忘录」应用中测试:

  1. 在「备忘录」应用中输入链接
  2. 长按该链接
  3. 选择「Open in [应用名称]」

在终端中测试:

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

3. 确认日志

// 确认深度链接接收情况
DeepLinkManager.shared.setDeepLinkListener { deepLinkData in
    print("""
    ==========================================
    딥링크 수신 테스트
    ==========================================
    Short Path: \(deepLinkData.shortPath)
    Link ID: \(deepLinkData.linkId)
    Source: \(deepLinkData.source.description)
    Parameters:
    \(deepLinkData.parameters.map { "  - \($0.key): \($0.value)" }.joined(separator: "\n"))
    ==========================================
    """)
}

参考资料


目录