StageUp
모바일 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가 이벤트 전송 시점에 자동으로 수집·포함하므로 별도로 설정할 필요가 없습니다.


트러블슈팅

1. Universal Link가 작동하지 않음

체크리스트:

  • 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

Notes 앱에서 테스트:

  1. Notes 앱에서 링크 입력
  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"))
    ==========================================
    """)
}

참고 자료


목차