adstage
Mobile SDKDeep Link

iOS

คู่มือการเชื่อมต่อ AdStage DeepLink (iOS)

สารบัญ

  1. ภาพรวม
  2. การตั้งค่าโปรเจกต์
  3. การตั้งค่า Info.plist
  4. การตั้งค่า AppDelegate
  5. การจัดการการรับ deep link
  6. การสร้าง deep link
  7. ฟีเจอร์ขั้นสูง
  8. การแก้ไขปัญหา

ภาพรวม

AdStage DeepLink SDK มีฟีเจอร์ดังต่อไปนี้:

  • Deep link แบบเรียลไทม์: ประมวลผลทันทีผ่าน URL Scheme และ Universal Links
  • Deferred deep link: กู้คืนอัตโนมัติเมื่อเปิดแอปครั้งแรกหลังการติดตั้ง
  • การสร้าง deep link แบบไดนามิก: สร้างลิงก์ที่ติดตามได้ผ่าน API ของเซิร์ฟเวอร์
  • การติดตาม attribution: การวิเคราะห์การตลาดบนพื้นฐานของพารามิเตอร์ 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
  • วางไว้ที่ path รากหรือในโฟลเดอร์ .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)
    }
}

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")
}
// 디퍼드 딥링크는 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(), เมธอดแบบ chaining ของ DeepLinkBuilder และ create(completion:) ทั้งหมดถูกเปิดเผยเป็น @objc จึงสามารถใช้งานได้โดยตรงจาก Objective-C คุณสามารถสร้าง wrapper อย่างง่ายที่รับเฉพาะสตริง Short URL ผ่าน callback ได้

// 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. การตั้งค่าแอตทริบิวต์ผู้ใช้แบบ global

หากตั้งค่าข้อมูลผู้ใช้ด้วย 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)
}

สาเหตุ:

  • การเก็บลายนิ้วมืออุปกรณ์ (device fingerprint) ล้มเหลว
  • การจับคู่ที่เซิร์ฟเวอร์ล้มเหลว
  • ปัญหาการเชื่อมต่อเครือข่าย

การตรวจสอบ:

// 디퍼드 딥링크는 initialize() 이후 자동 처리됩니다(수동 트리거 API 없음).
// 매칭 결과는 setDeepLinkListener 콜백으로 전달되므로 리스너에서 로그를 확인하세요.
AdStageManager.shared.setDeepLinkListener { data in
    print("✅ 디퍼드 딥링크 수신: \(data)")
}

การตั้งค่า 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

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. การตรวจสอบ log

// 딥링크 수신 확인
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"))
    ==========================================
    """)
}

แหล่งข้อมูลอ้างอิง


สารบัญ

คู่มือการเชื่อมต่อ AdStage DeepLink (iOS)สารบัญภาพรวมการตั้งค่าโปรเจกต์การตั้งค่า CocoaPodsการตั้งค่า Info.plist1. การตั้งค่า URL Scheme2. การตั้งค่า Universal Links3. ไฟล์ Apple App Site Association4. การตั้งค่าสิทธิ์ที่จำเป็นการตั้งค่า AppDelegateSwift - AppDelegate.swiftSwiftUI - โครงสร้าง Appการจัดการการรับ deep link1. การจัดการ deep link แบบเรียลไทม์2. Deferred deep link (การจัดการอัตโนมัติ)3. การจัดการตามแหล่งที่มาการสร้าง deep link1. รูปแบบ Builder2. วิธี async/await3. วิธีที่เข้ากันได้กับ Objective-Cฟีเจอร์ขั้นสูง1. การตั้งค่าแอตทริบิวต์ผู้ใช้แบบ globalการแก้ไขปัญหา1. Universal Link ไม่ทำงาน2. URL Scheme ไม่ทำงาน3. Deferred deep link ไม่ทำงาน4. ไม่ได้รับ deep link เมื่อแอปอยู่ในเบื้องหลัง5. การตรวจสอบ Build Settingsวิธีทดสอบ1. การทดสอบ URL Scheme2. การทดสอบ Universal Link3. การตรวจสอบ logแหล่งข้อมูลอ้างอิง