├── Docs ├── images │ └── apple_prefix_id.png └── UniversalLink.md ├── AppAttributionDemo ├── AppAttributionDemo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── SceneDelegate.swift │ ├── ViewController.swift │ └── AppDelegate.swift ├── AppAttributionDemo.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── AppAttributionDemo.xcscheme │ └── project.pbxproj ├── AppAttributionDemo.xcworkspace │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── contents.xcworkspacedata ├── Podfile └── Podfile.lock ├── .gitignore └── README.md /Docs/images/apple_prefix_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DLinkSDK/client-sdk-swift/HEAD/Docs/images/apple_prefix_id.png -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AppAttributionDemo/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | source 'https://github.com/DLinkSDK/deeplink-dev-specs.git' 3 | 4 | platform :ios, '13.0' 5 | 6 | target 'AppAttributionDemo' do 7 | # Comment the next line if you don't want to use dynamic frameworks 8 | use_frameworks! 9 | 10 | # Pods for AppAttributionDemo 11 | pod 'AppAttribution', '~>3.0.4' 12 | # [Optional] if you want to support GoogleAdsOnDeviceConversion 13 | pod 'AppAttribution_GoogleAdsOnDevice', '~>3.0.4' 14 | 15 | pod 'AppsFlyerFramework', '6.12.1' 16 | end 17 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | UISceneStoryboardFile 19 | Main 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Docs/UniversalLink.md: -------------------------------------------------------------------------------- 1 | #Universal Link 2 | 3 | ## Prepare for iOS Universal Links 4 | 5 | ### Getting the app bundle ID and prefix ID 6 | 1. Log into your Apple Developer Account. 7 | 1. On the left-hand menu, select Certificates, Identifiers & Profiles. 8 | 1. Under Identifiers, select App IDs. 9 | 1. Click the relevant app. 10 | 1. Copy the prefix ID and app bundle ID. 11 | 1. Give the prefix ID and app bundle ID to your marketer. 12 | ![GetApplePrefixID](images/apple_prefix_id.png) 13 | 14 | ### Enabling associated domains 15 | To support associated domains in your app: 16 | 17 | Follow the [iOS instructions](https://developer.apple.com/documentation/xcode/supporting-associated-domains#Add-the-associated-domains-entitlement-to-your-app) to add the associated domains entitlement to your app 18 | 19 | ### Register applinks domain 20 | 1. Get the subdomain for your app from your marketer. 21 | 1. In Xcode, click on your project. 22 | 1. Click on the project target. 23 | 1. Switch to the Capabilities tab. 24 | 1. Turn on Associated Domain. 25 | 1. Add the subdomain that you got from your marketer. 26 | The format is applinks:**YOUR\_SUB\_DOMAIN** -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | #Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // AppAttributionDemo 4 | // 5 | // 6 | 7 | import UIKit 8 | 9 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 10 | 11 | var window: UIWindow? 12 | 13 | 14 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 15 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 16 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 17 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 18 | guard let _ = (scene as? UIWindowScene) else { return } 19 | } 20 | 21 | func sceneDidDisconnect(_ scene: UIScene) { 22 | // Called as the scene is being released by the system. 23 | // This occurs shortly after the scene enters the background, or when its session is discarded. 24 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 25 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 26 | } 27 | 28 | func sceneDidBecomeActive(_ scene: UIScene) { 29 | // Called when the scene has moved from an inactive state to an active state. 30 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 31 | } 32 | 33 | func sceneWillResignActive(_ scene: UIScene) { 34 | // Called when the scene will move from an active state to an inactive state. 35 | // This may occur due to temporary interruptions (ex. an incoming phone call). 36 | } 37 | 38 | func sceneWillEnterForeground(_ scene: UIScene) { 39 | // Called as the scene transitions from the background to the foreground. 40 | // Use this method to undo the changes made on entering the background. 41 | } 42 | 43 | func sceneDidEnterBackground(_ scene: UIScene) { 44 | // Called as the scene transitions from the foreground to the background. 45 | // Use this method to save data, release shared resources, and store enough scene-specific state information 46 | // to restore the scene back to its current state. 47 | } 48 | 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /AppAttributionDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.10.2) 3 | - AppAttribution (3.0.4): 4 | - Alamofire 5 | - CocoaAsyncSocket 6 | - DLinkAuthKit (>= 1.0.1) 7 | - MMKV 8 | - SweetSugar (>= 1.1.1) 9 | - AppAttribution_GoogleAdsOnDevice (3.0.4): 10 | - AppAttribution (~> 3.0.4) 11 | - GoogleAdsOnDeviceConversion 12 | - AppsFlyerFramework (6.12.1): 13 | - AppsFlyerFramework/Main (= 6.12.1) 14 | - AppsFlyerFramework/Main (6.12.1) 15 | - CocoaAsyncSocket (7.6.5) 16 | - CryptoSwift (1.8.4) 17 | - DLinkAuthKit (1.0.1): 18 | - CryptoSwift 19 | - GoogleAdsOnDeviceConversion (3.2.0): 20 | - GoogleUtilities/Environment (~> 8.1) 21 | - GoogleUtilities/Logger (~> 8.1) 22 | - GoogleUtilities/Network (~> 8.1) 23 | - nanopb (~> 3.30910.0) 24 | - GoogleUtilities/Environment (8.1.0): 25 | - GoogleUtilities/Privacy 26 | - GoogleUtilities/Logger (8.1.0): 27 | - GoogleUtilities/Environment 28 | - GoogleUtilities/Privacy 29 | - GoogleUtilities/Network (8.1.0): 30 | - GoogleUtilities/Logger 31 | - "GoogleUtilities/NSData+zlib" 32 | - GoogleUtilities/Privacy 33 | - GoogleUtilities/Reachability 34 | - "GoogleUtilities/NSData+zlib (8.1.0)": 35 | - GoogleUtilities/Privacy 36 | - GoogleUtilities/Privacy (8.1.0) 37 | - GoogleUtilities/Reachability (8.1.0): 38 | - GoogleUtilities/Logger 39 | - GoogleUtilities/Privacy 40 | - MMKV (2.3.0): 41 | - MMKVCore (~> 2.3.0) 42 | - MMKVCore (2.3.0) 43 | - nanopb (3.30910.0): 44 | - nanopb/decode (= 3.30910.0) 45 | - nanopb/encode (= 3.30910.0) 46 | - nanopb/decode (3.30910.0) 47 | - nanopb/encode (3.30910.0) 48 | - SweetSugar (1.1.1) 49 | 50 | DEPENDENCIES: 51 | - AppAttribution (~> 3.0.4) 52 | - AppAttribution_GoogleAdsOnDevice (~> 3.0.4) 53 | - AppsFlyerFramework (= 6.12.1) 54 | 55 | SPEC REPOS: 56 | https://github.com/CocoaPods/Specs.git: 57 | - Alamofire 58 | - AppsFlyerFramework 59 | - CocoaAsyncSocket 60 | - CryptoSwift 61 | - GoogleAdsOnDeviceConversion 62 | - GoogleUtilities 63 | - MMKV 64 | - MMKVCore 65 | - nanopb 66 | - SweetSugar 67 | https://github.com/DLinkSDK/deeplink-dev-specs.git: 68 | - AppAttribution 69 | - AppAttribution_GoogleAdsOnDevice 70 | - DLinkAuthKit 71 | 72 | SPEC CHECKSUMS: 73 | Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496 74 | AppAttribution: d92e9550751bb35540984e0c893b79217b4d6889 75 | AppAttribution_GoogleAdsOnDevice: 872370260ab92fddddca59760f10532063ede353 76 | AppsFlyerFramework: e29b63fc5441400a38a31c5501c1da500b9d53d0 77 | CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 78 | CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90 79 | DLinkAuthKit: e9066f09131dff806fb0e107c779329012d64d70 80 | GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f 81 | GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 82 | MMKV: c953dbaac0da392c24b005e763c03ce2638b4ed7 83 | MMKVCore: d078dce7d6586a888b2c2ef5343b6242678e3ee8 84 | nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 85 | SweetSugar: 40d60b273b866b8d69d177a62c45b9e4b93cb03c 86 | 87 | PODFILE CHECKSUM: 74970cf3d815de46f57c1059aa75f3c7c5ab3df3 88 | 89 | COCOAPODS: 1.16.2 90 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo.xcodeproj/xcshareddata/xcschemes/AppAttributionDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // AppAttributionDemo 4 | // 5 | // 6 | 7 | import UIKit 8 | import AppAttribution 9 | 10 | class ViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | // Do any additional setup after loading the view. 15 | 16 | // here's demo for log attribution events with AppAttribution. 17 | } 18 | 19 | private func setUserInfo() { 20 | var userData = UserDataModel() 21 | userData.countryName = "COUNTRY_NAME" 22 | userData.city = "CITY" 23 | userData.emails = ["EMAIL1", "EMAIL2"] 24 | userData.phones = ["PHONE1", "PHONE2"] 25 | userData.firstName = "FIRST_NAME" 26 | userData.lastName = "LAST_NAME" 27 | userData.fbLoginId = "FB_LOGIN_ID" 28 | AttributionManager.setUserData(userData) 29 | } 30 | 31 | private func generateRandomProductContent(_ count: Int, price: Float, skuId: String? = nil) -> ProductItem.Content { 32 | let suffix = (0..<10).randomElement() ?? 0 33 | var content = ProductItem.Content( 34 | productId: "PRODUCT_ID_\(suffix)", 35 | productName: "PRODUCT_NAME_\(suffix)", 36 | quantity: count, 37 | value: price 38 | ) 39 | 40 | // if the content has an related skuid, set it up. 41 | if let skuId { 42 | content.skuId = skuId 43 | } 44 | 45 | return content 46 | } 47 | 48 | private func logViewContent() { 49 | // you can log this when users entry the product's detail page 50 | let productItem = ProductItem( 51 | content: generateRandomProductContent(1, price: 9.9), 52 | currency: "USD", 53 | value: 9.9 // total price 54 | ) 55 | LogEvent.shared.logViewContentEvent(item: productItem) 56 | } 57 | 58 | private func logAddToCart() { 59 | // when the user clicks the "Add to Cart" button. 60 | let content = generateRandomProductContent(3, price: 10.0) 61 | let productItem = ProductItem( 62 | content: content, 63 | currency: "RMB", 64 | value: content.value * Float(content.quantity) // total price 65 | ) 66 | LogEvent.shared.logAddToCart(item: productItem) 67 | } 68 | 69 | private func logAddToWishlist() { 70 | // when the user clicks the "Add to wishlist" button. 71 | let content = generateRandomProductContent(3, price: 10.0) 72 | let productItem = ProductItem( 73 | content: content, 74 | currency: "RMB", 75 | value: content.value * Float(content.quantity) // total price 76 | ) 77 | LogEvent.shared.logAddToWishList(item: productItem) 78 | } 79 | 80 | private func logInitiateCheckoutEvent() { 81 | // when the user start to checkout 82 | let productItem = ProductItem( 83 | content: generateRandomProductContent(1, price: 9.9), 84 | currency: "USD", 85 | value: 9.9 // total price 86 | ) 87 | LogEvent.shared.logInitiateCheckoutEvent(item: productItem) 88 | } 89 | 90 | private func logPurchase() { 91 | // when the user complte a normal purchase. 92 | 93 | // create the product content 94 | // if it have an related skuId, remember to set it up like content.skuId = "xxxx" 95 | var content = generateRandomProductContent(1, price: 9.9, skuId: "my_product_sku_Id") 96 | content.purchaseDate = Int(Date().timeIntervalSince1970) // set up the purchase date, measure by second 97 | let productItem = ProductItem( 98 | content: content, 99 | currency: "USD", 100 | value: 9.9 // total price 101 | ) 102 | // pass the transactionIdentfier in if it's available. 103 | LogEvent.shared.logPurchaseSuccess(item: productItem, transactionIdentifier: "purchase_transaction_id") 104 | } 105 | 106 | private func logSubscribe() { 107 | // when the user has complete a subsciption. 108 | 109 | // create the product content 110 | // if it have an related skuId, remember to set it up like content.skuId = "xxxx" 111 | var content = generateRandomProductContent(1, price: 9.9, skuId: "my_subscirption_product_sku_Id") 112 | content.purchaseDate = Int(Date().timeIntervalSince1970) // set up the purchase date, measure by second 113 | var productItem = ProductItem( 114 | content: content, 115 | currency: "USD", 116 | value: 9.9 // total price 117 | ) 118 | // Required!! set up the period of a subscription. 119 | // in other events, if the product is a subscription, you'd better also setup subscribeDay 120 | productItem.subscribeDay = 30 121 | // pass the transactionIdentfier in if it's available. 122 | LogEvent.shared.logPurchaseSuccess(item: productItem, transactionIdentifier: "subscription_transaction_id") 123 | 124 | } 125 | } 126 | 127 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # client-sdk-swift 2 | 3 | ## Step 1: Get the AccountId and DevToken 4 | 5 | Register an account at [https://console.dlink.cloud/](https://console.dlink.cloud). After creating an app on the platform, get the corresponding AccountId of the app. 6 | 7 | ## Step 2: Get the SDK 8 | 9 | ### (1) Add Pod source in you Pod file 10 | 11 | ```Ruby 12 | source 'https://github.com/CocoaPods/Specs.git' 13 | source 'https://github.com/DLinkSDK/deeplink-dev-specs.git' 14 | ``` 15 | 16 | ### (2) add dependency 17 | ```Ruby 18 | pod 'AppAttribution' 19 | ``` 20 | 21 | ## Step 3: Initialize the SDK 22 | 23 | ### (1) configure and setup AttributionManager and start it 24 | ```swift 25 | import AppAttribution 26 | 27 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 28 | 29 | // you can set you project-applied custom deviceid 30 | AttributionManager.setCustomDeviceId("your device id here") 31 | // create your configuration 32 | // appleStoreId is your app id in the AppleStore, use format idxxx, like facebook's appleStoreId is id284882215 33 | var configuration = AttributionConfiguration(accountId: "your_account_id", devToken: "your_dev_token", appleStoreId: "idxxxxxxx") 34 | AttributionManager.setup(configuration: configuration, delegate: self) // setup your appid 35 | // start attribution manager 36 | AttributionManager.start() 37 | 38 | return true 39 | } 40 | ``` 41 | 42 | ### (2) **[!! IMPORTANT !!] Allow to make app attribution report** 43 | ```swift 44 | // if you're ready to report app attribution, call readyToReport to start the process 45 | // strongly recommended to begin report after you get your att auth 46 | 47 | AttributionManager.readyToReport() 48 | ``` 49 | ### (3) [Optional] UniversalLink 50 | 1. check Associated Domains is enabled in your app. 51 | 2. ask DLink to get your applinks domain. add the applinks domain to your Associated Domains. 52 | 3. pass the universal link info to AttributionManager in UIApplicationDelegate 53 | [How to enable Universal Link](./Docs/UniversalLink.md) 54 | 55 | ```swift 56 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool { 57 | // If you use universal link to open your app 58 | // pass the info to AttributionManager 59 | AttributionManager.application(application: application, continue: userActivity, restorationHandler: restorationHandler) 60 | return true 61 | } 62 | 63 | ``` 64 | 65 | ### (4) Implement the delegate 66 | ```swift 67 | extension AppDelegate: AttributionManagerDelegate { 68 | func appAttributionDidFinish(matched: Bool, info: [String : Any]) { 69 | // app attribution finished 70 | if matched { 71 | print("attribution matched, get info \(info)") 72 | // you can fetch your campaign info from here 73 | if let campaignInfo = info[AttributionParamKey.campaignInfoKey] as? [String: Any] { 74 | // check your campaign info params here 75 | print("campaign info \(campaignInfo)") 76 | } 77 | } else { 78 | print("attribution not matched") 79 | } 80 | } 81 | 82 | func appAttributionDidFail(error: any Error) { 83 | print("attribution failed \(error)") 84 | } 85 | 86 | func appAttributionDidReceiveUniversalLinkInfo(_ info: [String : Any]) { 87 | // universal link info 88 | print("get info from universal link \(info)") 89 | } 90 | 91 | } 92 | ``` 93 | 94 | ### (5) Directly obtain attribution results 95 | 96 | You may also obtain the attribution result by attributionInfo API. 97 | Remeber it's only available after you have finished your attribution process, or else you can only get an nil result 98 | ```swift 99 | let info = AttributionManager.attributionInfo 100 | print("attribution info \(info)") 101 | ``` 102 | ## Step 4: AppsFlyerLib Support 103 | If your app supports AppsFlyerLib, follow the next steps 104 | ### (1) Setup adapter in configuration 105 | ```swift 106 | var configuration = AttributionConfiguration(appId: "your_app_id") 107 | configuration.appsFlyerAdapter = self // set AppsFlyerAdapter 108 | AttributionManager.setup(configuration: configuration, delegate: self) // set 109 | ``` 110 | 111 | ### (2) Implements AppsFlyerAdapter Protocol 112 | ```swift 113 | extension AppDelegate: AppsFlyerAdapter { 114 | func startAppsFlyerLib() { 115 | // Start Your AppsFlyerLib Here 116 | 117 | AppsFlyerLib.shared().appsFlyerDevKey = "your_apps_flyer_dev_key" 118 | AppsFlyerLib.shared().appleAppID = "your_apps_app_id" 119 | AppsFlyerLib.shared().start() 120 | } 121 | 122 | func getAppsFlyerUID() -> String { 123 | return AppsFlyerLib.shared().getAppsFlyerUID() 124 | } 125 | 126 | func logEvent(_ eventName: String, withValues values: [AnyHashable : Any]?) { 127 | AppsFlyerLib.shared().logEvent(eventName, withValues: values) 128 | } 129 | } 130 | ``` 131 | 132 | ### (3) Pass AppsFlyerLib conversation data to AttributionManager 133 | **This step is very important!** 134 | ```swift 135 | extension AppDelegate: AppsFlyerLibDelegate { 136 | func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) { 137 | // callback to AttributionManager with appsflyer result 138 | AttributionManager.onAppflyerConversionDataSuccess(conversionInfo) 139 | 140 | } 141 | 142 | func onConversionDataFail(_ error: any Error) { 143 | AttributionManager.onAppflyerConversionDataFail(error) 144 | } 145 | } 146 | 147 | ``` 148 | 149 | ## GoogleAdsOnDeviceConversion Support 150 | 1. If you want to add GoogleAdsOnDeviceConversion support to dlink, add the podspec below 151 | ```ruby 152 | pod 'AppAttribution_GoogleAdsOnDevice' 153 | ``` 154 | 2. enable the feature before setup your configuration 155 | ```swift 156 | import AppAttribution_GoogleAdsOnDevice 157 | 158 | AttributionManager.enableGoogleAdsOnDeviceConversion() 159 | ``` 160 | 161 | 162 | ## Check Detail APIs 163 | [API Documents](https://deeplink-dev-ios.s3.ap-southeast-1.amazonaws.com/ios-frameworks/AppAttribution/docs/documentation/appattribution/index.html) 164 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // AppAttributionDemo 4 | // 5 | 6 | import UIKit 7 | import AppAttribution 8 | import AppAttribution_GoogleAdsOnDevice // [Optional] If you use GoogleAdsOnDeviceConversion 9 | import AppTrackingTransparency 10 | import AppsFlyerLib 11 | 12 | @main 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | 18 | // [Optional] you can set you project-applied custom deviceid 19 | // or use the device id we generate for you 20 | // AttributionManager.setCustomDeviceId("your device id here") 21 | // you can set your app's user id to associate the user info 22 | AttributionManager.setUserId("your_user_id") 23 | // [Optional] you can enable GoogleAdsOnDeviceConversion support for dlink 24 | AttributionManager.enableGoogleAdsOnDeviceConversion() 25 | // create your configuration 26 | // appleStoreId is your app id in AppleStore 27 | var configuration = AttributionConfiguration(accountId: "your_account_id", devToken: "your_dev_token", appleStoreId: "idxxxxxxx") 28 | // if you want to support appsflyerlib 29 | // setup adapter here 30 | configuration.appsFlyerAdapter = self 31 | configuration.logEnabled = true 32 | AttributionManager.logDelegate = self 33 | AttributionManager.setup(configuration: configuration, delegate: self) // set your appid 34 | // start attribution manager 35 | AttributionManager.start() 36 | 37 | // you can call AttributionManager.readyToReport to 38 | // report your app attribution immediately 39 | // but we strongly recommend you to call readyToReport 40 | // after you finish your ATT auth 41 | 42 | // do something, such as request for att 43 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in 44 | // delay for some seconds, and request att 45 | self?.requestATTAuth() 46 | } 47 | return true 48 | } 49 | 50 | private func requestATTAuth() { 51 | if #available(iOS 14, *) { 52 | print("ask for att auth") 53 | ATTrackingManager.requestTrackingAuthorization { status in 54 | // auth determined 55 | // we can start to report app attribution 56 | print("auth finished, ready to report app attribution") 57 | Task { @MainActor in 58 | AttributionManager.readyToReport() 59 | } 60 | } 61 | } else { 62 | // Fallback on earlier versions 63 | print("directly report app attribution") 64 | AttributionManager.readyToReport() 65 | } 66 | } 67 | 68 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool { 69 | // If you use universal link to open your app 70 | // pass the info to AttributionManager 71 | AttributionManager.application(application: application, continue: userActivity, restorationHandler: restorationHandler) 72 | return true 73 | } 74 | 75 | // MARK: UISceneSession Lifecycle 76 | 77 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 78 | // Called when a new scene session is being created. 79 | // Use this method to select a configuration to create the new scene with. 80 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 81 | } 82 | 83 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 84 | // Called when the user discards a scene session. 85 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 86 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 87 | } 88 | 89 | 90 | } 91 | 92 | // MARK: - AttributionManagerDelegate 93 | extension AppDelegate: AttributionManagerDelegate { 94 | func appAttributionDidReceiveUniversalLinkInfo(_ info: [String : Any]) { 95 | // universal link info 96 | print("get info from universal link \(info)") 97 | } 98 | 99 | func appAttributionDidFinish(matched: Bool, info: [String : Any]) { 100 | // app attribution finished 101 | if matched { 102 | print("attribution matched, get info \(info)") 103 | // you can fetch your campaign info from here 104 | if let campaignInfo = info[AttributionParamKey.campaignInfoKey] as? [String: Any] { 105 | // check your campaign info params here 106 | print("campaign info \(campaignInfo)") 107 | } 108 | } else { 109 | print("attribution not matched") 110 | } 111 | } 112 | 113 | func appAttributionDidFail(error: any Error) { 114 | print("attribution failed \(error)") 115 | } 116 | } 117 | 118 | // MARK: - AppAttribution Log Delegate 119 | extension AppDelegate: AttributionManagerLogDelegate { 120 | func log(message: String) { 121 | print(message) 122 | } 123 | } 124 | 125 | // MARK: - AppsFlyerAdapter 126 | extension AppDelegate: AppsFlyerAdapter { 127 | func startAppsFlyerLib() { 128 | // Start Your AppsFlyerLib Here 129 | print("start appsflyer") 130 | AppsFlyerLib.shared().appsFlyerDevKey = "your_apps_flyer_dev_key" 131 | AppsFlyerLib.shared().appleAppID = "your_apps_app_id" 132 | AppsFlyerLib.shared().start() 133 | AppsFlyerLib.shared().delegate = self 134 | } 135 | 136 | func getAppsFlyerUID() -> String { 137 | return AppsFlyerLib.shared().getAppsFlyerUID() 138 | } 139 | 140 | func logEvent(_ eventName: String, withValues values: [AnyHashable : Any]?) { 141 | AppsFlyerLib.shared().logEvent(eventName, withValues: values) 142 | } 143 | } 144 | 145 | // MARK: - AppsFlyerLibDelegate 146 | extension AppDelegate: AppsFlyerLibDelegate { 147 | func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) { 148 | print("appsflyer get conversion \(conversionInfo)") 149 | // callback to AttributionManager with appsflyer result 150 | AttributionManager.onAppflyerConversionDataSuccess(conversionInfo) 151 | } 152 | 153 | func onConversionDataFail(_ error: any Error) { 154 | print("appsflyer failed \(error)") 155 | AttributionManager.onAppflyerConversionDataFail(error) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /AppAttributionDemo/AppAttributionDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7FC760FF2D5F4A6400FED6AD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FC760FE2D5F4A6400FED6AD /* AppDelegate.swift */; }; 11 | 7FC761012D5F4A6400FED6AD /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FC761002D5F4A6400FED6AD /* SceneDelegate.swift */; }; 12 | 7FC761032D5F4A6400FED6AD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FC761022D5F4A6400FED6AD /* ViewController.swift */; }; 13 | 7FC761062D5F4A6400FED6AD /* Base in Resources */ = {isa = PBXBuildFile; fileRef = 7FC761052D5F4A6400FED6AD /* Base */; }; 14 | 7FC761082D5F4A6500FED6AD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7FC761072D5F4A6500FED6AD /* Assets.xcassets */; }; 15 | 7FC7610B2D5F4A6500FED6AD /* Base in Resources */ = {isa = PBXBuildFile; fileRef = 7FC7610A2D5F4A6500FED6AD /* Base */; }; 16 | 8BE4D0BD1ED1EF2C33F797C9 /* Pods_AppAttributionDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B260241DCCB141AC8C539EC4 /* Pods_AppAttributionDemo.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 1392004F73B038519FB2F9E4 /* Pods-AppAttributionDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttributionDemo.debug.xcconfig"; path = "Target Support Files/Pods-AppAttributionDemo/Pods-AppAttributionDemo.debug.xcconfig"; sourceTree = ""; }; 21 | 7FC760FB2D5F4A6400FED6AD /* AppAttributionDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppAttributionDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 7FC760FE2D5F4A6400FED6AD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 7FC761002D5F4A6400FED6AD /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 24 | 7FC761022D5F4A6400FED6AD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | 7FC761052D5F4A6400FED6AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 7FC761072D5F4A6500FED6AD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | 7FC7610A2D5F4A6500FED6AD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | 7FC7610C2D5F4A6500FED6AD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 7FEF35462EC5E7D200F9A7DD /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = SOURCE_ROOT; }; 30 | A198746E676B7DD2B58B72D5 /* Pods-AppAttributionDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppAttributionDemo.release.xcconfig"; path = "Target Support Files/Pods-AppAttributionDemo/Pods-AppAttributionDemo.release.xcconfig"; sourceTree = ""; }; 31 | B260241DCCB141AC8C539EC4 /* Pods_AppAttributionDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppAttributionDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | 7FC760F82D5F4A6400FED6AD /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | 8BE4D0BD1ED1EF2C33F797C9 /* Pods_AppAttributionDemo.framework in Frameworks */, 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 0902C3577FB4203B4BDA98A5 /* Frameworks */ = { 47 | isa = PBXGroup; 48 | children = ( 49 | B260241DCCB141AC8C539EC4 /* Pods_AppAttributionDemo.framework */, 50 | ); 51 | name = Frameworks; 52 | sourceTree = ""; 53 | }; 54 | 50F499FA62CE363F09BAFC1D /* Pods */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 1392004F73B038519FB2F9E4 /* Pods-AppAttributionDemo.debug.xcconfig */, 58 | A198746E676B7DD2B58B72D5 /* Pods-AppAttributionDemo.release.xcconfig */, 59 | ); 60 | path = Pods; 61 | sourceTree = ""; 62 | }; 63 | 7FC760F22D5F4A6400FED6AD = { 64 | isa = PBXGroup; 65 | children = ( 66 | 7FEF35462EC5E7D200F9A7DD /* README.md */, 67 | 7FC760FD2D5F4A6400FED6AD /* AppAttributionDemo */, 68 | 7FC760FC2D5F4A6400FED6AD /* Products */, 69 | 50F499FA62CE363F09BAFC1D /* Pods */, 70 | 0902C3577FB4203B4BDA98A5 /* Frameworks */, 71 | ); 72 | sourceTree = ""; 73 | }; 74 | 7FC760FC2D5F4A6400FED6AD /* Products */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 7FC760FB2D5F4A6400FED6AD /* AppAttributionDemo.app */, 78 | ); 79 | name = Products; 80 | sourceTree = ""; 81 | }; 82 | 7FC760FD2D5F4A6400FED6AD /* AppAttributionDemo */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 7FC760FE2D5F4A6400FED6AD /* AppDelegate.swift */, 86 | 7FC761002D5F4A6400FED6AD /* SceneDelegate.swift */, 87 | 7FC761022D5F4A6400FED6AD /* ViewController.swift */, 88 | 7FC761042D5F4A6400FED6AD /* Main.storyboard */, 89 | 7FC761072D5F4A6500FED6AD /* Assets.xcassets */, 90 | 7FC761092D5F4A6500FED6AD /* LaunchScreen.storyboard */, 91 | 7FC7610C2D5F4A6500FED6AD /* Info.plist */, 92 | ); 93 | path = AppAttributionDemo; 94 | sourceTree = ""; 95 | }; 96 | /* End PBXGroup section */ 97 | 98 | /* Begin PBXNativeTarget section */ 99 | 7FC760FA2D5F4A6400FED6AD /* AppAttributionDemo */ = { 100 | isa = PBXNativeTarget; 101 | buildConfigurationList = 7FC7610F2D5F4A6500FED6AD /* Build configuration list for PBXNativeTarget "AppAttributionDemo" */; 102 | buildPhases = ( 103 | 1D14131E3B6B6E2720C827F4 /* [CP] Check Pods Manifest.lock */, 104 | 7FC760F72D5F4A6400FED6AD /* Sources */, 105 | 7FC760F82D5F4A6400FED6AD /* Frameworks */, 106 | 7FC760F92D5F4A6400FED6AD /* Resources */, 107 | 0F6247415D5A052767295EE2 /* [CP] Embed Pods Frameworks */, 108 | ); 109 | buildRules = ( 110 | ); 111 | dependencies = ( 112 | ); 113 | name = AppAttributionDemo; 114 | productName = AppAttributionDemo; 115 | productReference = 7FC760FB2D5F4A6400FED6AD /* AppAttributionDemo.app */; 116 | productType = "com.apple.product-type.application"; 117 | }; 118 | /* End PBXNativeTarget section */ 119 | 120 | /* Begin PBXProject section */ 121 | 7FC760F32D5F4A6400FED6AD /* Project object */ = { 122 | isa = PBXProject; 123 | attributes = { 124 | BuildIndependentTargetsInParallel = 1; 125 | LastSwiftUpdateCheck = 1530; 126 | LastUpgradeCheck = 1530; 127 | TargetAttributes = { 128 | 7FC760FA2D5F4A6400FED6AD = { 129 | CreatedOnToolsVersion = 15.3; 130 | }; 131 | }; 132 | }; 133 | buildConfigurationList = 7FC760F62D5F4A6400FED6AD /* Build configuration list for PBXProject "AppAttributionDemo" */; 134 | compatibilityVersion = "Xcode 14.0"; 135 | developmentRegion = en; 136 | hasScannedForEncodings = 0; 137 | knownRegions = ( 138 | en, 139 | Base, 140 | ); 141 | mainGroup = 7FC760F22D5F4A6400FED6AD; 142 | productRefGroup = 7FC760FC2D5F4A6400FED6AD /* Products */; 143 | projectDirPath = ""; 144 | projectRoot = ""; 145 | targets = ( 146 | 7FC760FA2D5F4A6400FED6AD /* AppAttributionDemo */, 147 | ); 148 | }; 149 | /* End PBXProject section */ 150 | 151 | /* Begin PBXResourcesBuildPhase section */ 152 | 7FC760F92D5F4A6400FED6AD /* Resources */ = { 153 | isa = PBXResourcesBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | 7FC761082D5F4A6500FED6AD /* Assets.xcassets in Resources */, 157 | 7FC7610B2D5F4A6500FED6AD /* Base in Resources */, 158 | 7FC761062D5F4A6400FED6AD /* Base in Resources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXResourcesBuildPhase section */ 163 | 164 | /* Begin PBXShellScriptBuildPhase section */ 165 | 0F6247415D5A052767295EE2 /* [CP] Embed Pods Frameworks */ = { 166 | isa = PBXShellScriptBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | ); 170 | inputFileListPaths = ( 171 | "${PODS_ROOT}/Target Support Files/Pods-AppAttributionDemo/Pods-AppAttributionDemo-frameworks-${CONFIGURATION}-input-files.xcfilelist", 172 | ); 173 | inputPaths = ( 174 | ); 175 | name = "[CP] Embed Pods Frameworks"; 176 | outputFileListPaths = ( 177 | "${PODS_ROOT}/Target Support Files/Pods-AppAttributionDemo/Pods-AppAttributionDemo-frameworks-${CONFIGURATION}-output-files.xcfilelist", 178 | ); 179 | outputPaths = ( 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | shellPath = /bin/sh; 183 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppAttributionDemo/Pods-AppAttributionDemo-frameworks.sh\"\n"; 184 | showEnvVarsInLog = 0; 185 | }; 186 | 1D14131E3B6B6E2720C827F4 /* [CP] Check Pods Manifest.lock */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputFileListPaths = ( 192 | ); 193 | inputPaths = ( 194 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 195 | "${PODS_ROOT}/Manifest.lock", 196 | ); 197 | name = "[CP] Check Pods Manifest.lock"; 198 | outputFileListPaths = ( 199 | ); 200 | outputPaths = ( 201 | "$(DERIVED_FILE_DIR)/Pods-AppAttributionDemo-checkManifestLockResult.txt", 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | shellPath = /bin/sh; 205 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 206 | showEnvVarsInLog = 0; 207 | }; 208 | /* End PBXShellScriptBuildPhase section */ 209 | 210 | /* Begin PBXSourcesBuildPhase section */ 211 | 7FC760F72D5F4A6400FED6AD /* Sources */ = { 212 | isa = PBXSourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 7FC761032D5F4A6400FED6AD /* ViewController.swift in Sources */, 216 | 7FC760FF2D5F4A6400FED6AD /* AppDelegate.swift in Sources */, 217 | 7FC761012D5F4A6400FED6AD /* SceneDelegate.swift in Sources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXSourcesBuildPhase section */ 222 | 223 | /* Begin PBXVariantGroup section */ 224 | 7FC761042D5F4A6400FED6AD /* Main.storyboard */ = { 225 | isa = PBXVariantGroup; 226 | children = ( 227 | 7FC761052D5F4A6400FED6AD /* Base */, 228 | ); 229 | name = Main.storyboard; 230 | sourceTree = ""; 231 | }; 232 | 7FC761092D5F4A6500FED6AD /* LaunchScreen.storyboard */ = { 233 | isa = PBXVariantGroup; 234 | children = ( 235 | 7FC7610A2D5F4A6500FED6AD /* Base */, 236 | ); 237 | name = LaunchScreen.storyboard; 238 | sourceTree = ""; 239 | }; 240 | /* End PBXVariantGroup section */ 241 | 242 | /* Begin XCBuildConfiguration section */ 243 | 7FC7610D2D5F4A6500FED6AD /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 248 | CLANG_ANALYZER_NONNULL = YES; 249 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 250 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_ENABLE_OBJC_WEAK = YES; 254 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 255 | CLANG_WARN_BOOL_CONVERSION = YES; 256 | CLANG_WARN_COMMA = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 259 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 260 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 261 | CLANG_WARN_EMPTY_BODY = YES; 262 | CLANG_WARN_ENUM_CONVERSION = YES; 263 | CLANG_WARN_INFINITE_RECURSION = YES; 264 | CLANG_WARN_INT_CONVERSION = YES; 265 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 266 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 269 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 270 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 271 | CLANG_WARN_STRICT_PROTOTYPES = YES; 272 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 273 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 274 | CLANG_WARN_UNREACHABLE_CODE = YES; 275 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 276 | COPY_PHASE_STRIP = NO; 277 | DEBUG_INFORMATION_FORMAT = dwarf; 278 | ENABLE_STRICT_OBJC_MSGSEND = YES; 279 | ENABLE_TESTABILITY = YES; 280 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 281 | GCC_C_LANGUAGE_STANDARD = gnu17; 282 | GCC_DYNAMIC_NO_PIC = NO; 283 | GCC_NO_COMMON_BLOCKS = YES; 284 | GCC_OPTIMIZATION_LEVEL = 0; 285 | GCC_PREPROCESSOR_DEFINITIONS = ( 286 | "DEBUG=1", 287 | "$(inherited)", 288 | ); 289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 290 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 291 | GCC_WARN_UNDECLARED_SELECTOR = YES; 292 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 293 | GCC_WARN_UNUSED_FUNCTION = YES; 294 | GCC_WARN_UNUSED_VARIABLE = YES; 295 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 296 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 297 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 298 | MTL_FAST_MATH = YES; 299 | ONLY_ACTIVE_ARCH = YES; 300 | SDKROOT = iphoneos; 301 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 302 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 303 | }; 304 | name = Debug; 305 | }; 306 | 7FC7610E2D5F4A6500FED6AD /* Release */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 311 | CLANG_ANALYZER_NONNULL = YES; 312 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_ENABLE_OBJC_WEAK = YES; 317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_COMMA = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_STRICT_PROTOTYPES = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 341 | ENABLE_NS_ASSERTIONS = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu17; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 353 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 354 | MTL_ENABLE_DEBUG_INFO = NO; 355 | MTL_FAST_MATH = YES; 356 | SDKROOT = iphoneos; 357 | SWIFT_COMPILATION_MODE = wholemodule; 358 | VALIDATE_PRODUCT = YES; 359 | }; 360 | name = Release; 361 | }; 362 | 7FC761102D5F4A6500FED6AD /* Debug */ = { 363 | isa = XCBuildConfiguration; 364 | baseConfigurationReference = 1392004F73B038519FB2F9E4 /* Pods-AppAttributionDemo.debug.xcconfig */; 365 | buildSettings = { 366 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 367 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 368 | CODE_SIGN_IDENTITY = "Apple Development"; 369 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 370 | CODE_SIGN_STYLE = Manual; 371 | CURRENT_PROJECT_VERSION = 1; 372 | DEVELOPMENT_TEAM = ""; 373 | "DEVELOPMENT_TEAM[sdk=iphoneos*]" = J7YC4K78XJ; 374 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 375 | GENERATE_INFOPLIST_FILE = YES; 376 | INFOPLIST_FILE = AppAttributionDemo/Info.plist; 377 | INFOPLIST_KEY_NSUserTrackingUsageDescription = "ATT Request Info"; 378 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 379 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 380 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 381 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 382 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 383 | LD_RUNPATH_SEARCH_PATHS = ( 384 | "$(inherited)", 385 | "@executable_path/Frameworks", 386 | ); 387 | MARKETING_VERSION = 1.0; 388 | PRODUCT_BUNDLE_IDENTIFIER = cn.hqz.AppAttributionDemo; 389 | PRODUCT_NAME = "$(TARGET_NAME)"; 390 | PROVISIONING_PROFILE_SPECIFIER = ""; 391 | "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = cn_hqz_dev; 392 | SWIFT_EMIT_LOC_STRINGS = YES; 393 | SWIFT_VERSION = 5.0; 394 | TARGETED_DEVICE_FAMILY = "1,2"; 395 | }; 396 | name = Debug; 397 | }; 398 | 7FC761112D5F4A6500FED6AD /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | baseConfigurationReference = A198746E676B7DD2B58B72D5 /* Pods-AppAttributionDemo.release.xcconfig */; 401 | buildSettings = { 402 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 403 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 404 | CODE_SIGN_IDENTITY = "Apple Development"; 405 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 406 | CODE_SIGN_STYLE = Manual; 407 | CURRENT_PROJECT_VERSION = 1; 408 | DEVELOPMENT_TEAM = ""; 409 | "DEVELOPMENT_TEAM[sdk=iphoneos*]" = J7YC4K78XJ; 410 | ENABLE_USER_SCRIPT_SANDBOXING = NO; 411 | GENERATE_INFOPLIST_FILE = YES; 412 | INFOPLIST_FILE = AppAttributionDemo/Info.plist; 413 | INFOPLIST_KEY_NSUserTrackingUsageDescription = "ATT Request Info"; 414 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 415 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 416 | INFOPLIST_KEY_UIMainStoryboardFile = Main; 417 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 418 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 419 | LD_RUNPATH_SEARCH_PATHS = ( 420 | "$(inherited)", 421 | "@executable_path/Frameworks", 422 | ); 423 | MARKETING_VERSION = 1.0; 424 | PRODUCT_BUNDLE_IDENTIFIER = cn.hqz.AppAttributionDemo; 425 | PRODUCT_NAME = "$(TARGET_NAME)"; 426 | PROVISIONING_PROFILE_SPECIFIER = ""; 427 | "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = cn_hqz_dev; 428 | SWIFT_EMIT_LOC_STRINGS = YES; 429 | SWIFT_VERSION = 5.0; 430 | TARGETED_DEVICE_FAMILY = "1,2"; 431 | }; 432 | name = Release; 433 | }; 434 | /* End XCBuildConfiguration section */ 435 | 436 | /* Begin XCConfigurationList section */ 437 | 7FC760F62D5F4A6400FED6AD /* Build configuration list for PBXProject "AppAttributionDemo" */ = { 438 | isa = XCConfigurationList; 439 | buildConfigurations = ( 440 | 7FC7610D2D5F4A6500FED6AD /* Debug */, 441 | 7FC7610E2D5F4A6500FED6AD /* Release */, 442 | ); 443 | defaultConfigurationIsVisible = 0; 444 | defaultConfigurationName = Release; 445 | }; 446 | 7FC7610F2D5F4A6500FED6AD /* Build configuration list for PBXNativeTarget "AppAttributionDemo" */ = { 447 | isa = XCConfigurationList; 448 | buildConfigurations = ( 449 | 7FC761102D5F4A6500FED6AD /* Debug */, 450 | 7FC761112D5F4A6500FED6AD /* Release */, 451 | ); 452 | defaultConfigurationIsVisible = 0; 453 | defaultConfigurationName = Release; 454 | }; 455 | /* End XCConfigurationList section */ 456 | }; 457 | rootObject = 7FC760F32D5F4A6400FED6AD /* Project object */; 458 | } 459 | --------------------------------------------------------------------------------