├── 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 | 
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 |
--------------------------------------------------------------------------------