diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/configure-deep-link-settings.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/configure-deep-link-settings.mdx new file mode 100644 index 000000000..eb85e5bec --- /dev/null +++ b/src/content/docs/sdk/ios/v4/features/deep-links/configure-deep-link-settings.mdx @@ -0,0 +1,110 @@ +--- +title: Configure deep link settings +description: Configure deep link settings for your app +slug: en/sdk/ios/v4/features/deep-links/configure-deep-link-settings +sidebar-position: 1 +versions: + - label: v5 + value: v5 + default: true + - label: v4 + value: v4 +redirects: + v5: /en/sdk/ios/features/deep-links/configure-deep-link-settings +--- + +
+## Create an app + +[Create an app](https://help.adjust.com/en/article/app-setup) in the Adjust dashboard. + +Please note that the Adjust dashboard only supports one bundle ID (`com.example.app`) and app scheme (`example://`) for each app. If your app uses different bundle IDs and/or app schemes for your release and debug builds, create a separate debug app. + +## Add iOS platform + +Add iOS in the platform settings for your app. This step requires entering the bundle ID. If you're not sure of the bundle ID for your app build, enter a temporary value (`com.example.app`) to save the platform settings. Follow the rest of this guide to collect all required data points, then return to platform settings in the dashboard to finish the configuration. + +
+## Set up a branded domain + +In the Adjust dashboard, [set up a branded domain](https://help.adjust.com/en/article/set-up-branded-domain) using Adjust's go.link domain (for example: `brandname.go.link`). + +If your iOS app and Android app use separate apps in Adjust, ensure that you select the same branded domain (`brandname.go.link`) for both apps in the Adjust dashboard. + +If you use separate apps for release and debug builds, create a separate branded domain for your debug app (`brandnamedebug.go.link`). If your debug apps use separate apps for iOS and Android in Adjust, ensure that you also select this same branded domain for both debug apps in the Adjust dashboard. + +Make note of your branded domains to configure in Xcode in the next section. + +
+## Configure settings in Xcode +
+### Configure universal links + +1. Open your Xcode project. +2. In the navigator pane, select the project name to access the project settings. +3. In the project settings, under **Targets**, select the appropriate target (usually your app's name). +4. Select the **Signing & Capabilities** tab. +5. Complete these steps for both the **Release** and **Debug** sub-tabs: + + - Make note of the value in the **Bundle Identifier** field. This is your app's bundle ID ("Release Bundle ID" or "Debug Bundle ID," respectively), which you need to configure in the iOS platform settings in the Adjust dashboard. + - In the **Associated Domains** section, add an entry for each of your branded domains. For the example domain `brandname.go.link`, here is the required entry: + + `applinks:brandname.go.link` + +**Troubleshoot missing or problematic Associated Domains settings** + +- If you don't see the **Associated Domains** section you might need to enable it first: + 1. Next to the **Release** or **Debug** sub-tab (wherever it's missing), click **+ Capability**. + 2. Search for "Associated Domains" and select it. +- If you get an error message, such as "Provisioning profile \{profile_name\} doesn't support the Associated Domains capability" when trying to enable Associated Domains, your provisioning profile likely needs to be updated: + - For automatic signing, make sure "Automatically manage signing" is selected at the top of the Signing & Capabilities page. + - For manual signing: + 1. Go to the Apple Developer portal and [enable the Associated Domains capability](https://developer.apple.com/help/account/manage-identifiers/enable-app-capabilities) for your app. + 2. [Download and import](https://help.apple.com/xcode/mac/current/#/dev1bf96f17e) the updated provisioning profile into Xcode. + +### Retrieve or configure app scheme + +App scheme is required for certain use cases where iOS doesn’t support universal links. You can reuse an existing app scheme for Adjust deep linking. + +1. In the Xcode navigator pane, select the project name to access the project settings. +2. Under Targets, select the appropriate target (usually your app’s name). +3. Select the **Info** tab. +4. Expand the **URL Types** section. + +If your app already has an app scheme, there will be URL Type entries, each of which has a single value for **URL Schemes**. Use the table below to determine your app schemes based on your URL Types configuration. Make note of the app schemes to configure them in the Adjust dashboard later. + +| **URL Schemes** field | App Scheme | +| ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Static value, such as `example` | `example://` is the "Release App Scheme" | +| Static values, such as `example`, `exampleDebug` in separate URL Types entries | `example://` is the "Release App Scheme"
`exampleDebug://` is the "Debug App Scheme" | +| Dynamic build setting variable, such as `$(APP_SCHEME)` | Go to **Build Settings** tab -> search for variable name, such as "APP_SCHEME" -> values for Release and Debug are the "Release App Scheme" and "Debug App Scheme," respectively | + +If you need to create an app scheme, follow these steps: + +- Select the **+** button to add a new URL Type. +- Fill in the following fields: + - **Identifier**: `$(PRODUCT_BUNDLE_IDENTIFIER)` + - **Role**: Editor + - **URL Schemes**: Enter your desired app scheme value (enter `example`, not `example://`). Don't use `http`, `https`, or reserved iOS schemes like `mailto`, `tel`, `sms`, or `facetime`. Entering a static value here will create a single app scheme used for both release and debug builds. +- Once created, make note of the app scheme for configuration in the Adjust dashboard later. + +
+## Retrieve App ID Prefix from Apple Developer Portal + +1. Log into the [Apple Developer portal](https://developer.apple.com/account/). +2. Under **Certificates, IDs & Profiles**, select **Identifiers**. +3. Select your app. +4. Near the top of the page, make note of the **App ID Prefix** to configure in the Adjust dashboard in the next section. +
+ +## Finish configuring iOS platform settings + +In the Adjust dashboard, finish configuring [iOS platform settings](https://help.adjust.com/en/article/platforms-ios-android-amazon-microsoft) for your apps using the below data points that you collected. + +| Data Point | Example | Requirement | +| ------------------ | ----------------- | ------------------------------------------------------------------ | +| Release Bundle ID | com.example.app | Required. | +| Debug Bundle ID | com.example.debug | Required if Debug Bundle ID is different than Release Bundle ID. | +| Release App Scheme | example:// | Required for use cases where iOS doesn't support universal links. | +| Debug App Scheme | exampleDebug:// | Required if Debug App Scheme is different than Release App Scheme. | +| App ID Prefix | ABCDE12345 | Required. | diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/data-points.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/data-points.mdx deleted file mode 100644 index d7c8b804a..000000000 --- a/src/content/docs/sdk/ios/v4/features/deep-links/data-points.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Retrieve data points -description: Retrieve the data required to set up deep links. -slug: en/sdk/ios/v4/features/deep-links/data-points -sidebar-position: 1 -versions: - - label: v5 - value: v5 - default: true - - label: v4 - value: v4 -redirects: - v5: /en/sdk/ios/features/deep-links/data-points ---- - -You need to retrieve the following data points before you can set up deep links in your app: - -- App ID Prefix -- Release Bundle ID -- Debug Bundle ID -- Release Custom URL Scheme -- Debug Custom URL Scheme -- Link resolution domain or domains - -## Instructions {#instructions} - -Follow these instructions to retrieve your data points. - -### App ID Prefix and Release Bundle ID {#app-id-prefix-and-release-bundle-id} - -Your App ID is found on the Apple Developer portal. It contains two parts: - -1. The **App ID prefix** -2. The **Bundle ID** - -The ID is formatted as `.`. For example: `ABC1234567.com.example.app` - -To find your App ID Prefix and Bundle ID, follow these steps: - -1. Log in to the [Apple Developer portal](https://developer.apple.com/account/). -2. Select **Certificates, IDs & Profiles** from the left-hand menu. -3. Select **Identifiers** from the left-hand menu. -4. Find your app and select it to open the edit page. -5. Your App ID Prefix and Bundle ID are displayed at the top of the page. Copy the relevant information and store it somewhere for later use. - -### Debug Bundle ID {#debug-bundle-id} - -If you're using a different bundle ID for your debug build, you can find its ID in Xcode. - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Signing & Capabilities** from the top menu. -5. Select **Debug** from the sub menu that appears. -6. Your Bundle ID is shown. Copy this information and store it somewhere for later use. - -### Custom URL schemes {#custom-url-schemes} - - - -A custom URL scheme is required for linking from other applications on the device, such as Telegram, X (formerly Twitter), and YouTube, or from push notifications. Check with your marketing team to see if a custom URL scheme is needed for the app. It's highly recommend to use the same custom URL scheme for iOS and Android. - - - -To retrieve your Custom URL Scheme, follow these steps: - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Info** from the top menu. -5. Expand the **URL Types** section and get the custom URL scheme. If the URL Schemes field contains a build setting (for example: `$(CUSTOM_URL_SCHEME)`), go to the build settings to retrieve the custom URL scheme values: - 1. Select **Build Settings** from the menu at the top. - 2. Find the setting named in the URL Schemes field and retrieve both the release and debug values. - -If your iOS app doesn't have a custom URL scheme yet, follow these steps to set a custom URL scheme: - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Info** from the top menu. -5. Expand the **URL Types** section. -6. Select the Add option to add a new URL type. -7. Fill in the following information to create a URL scheme: - - **Identifier**: `$(PRODUCT_BUNDLE_IDENTIFIER)` - - **URL Schemes**: your custom URL scheme. This must be unique. Don't use protected schemes such as `http`, `https`, or `mailto` - - **Role**: Editor - -This scheme will work for your production **and** debug builds. - -### Link Resolution domains {#link-resolution-domains} - - - -A link resolution domain is required for deep linking via email, SMS, QR codes, and platforms that shorten links. Check with your marketing team to see if [link resolution](https://help.adjust.com/en/article/link-resolution) is needed for the app. - - - -Your marketing team may already be using a link resolution domain for their email marketing platform. Get this domain from them and store it somewhere for later use. diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/deep-link.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/deep-link.mdx deleted file mode 100644 index 671b4261d..000000000 --- a/src/content/docs/sdk/ios/v4/features/deep-links/deep-link.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Enable deep links in Adjust -description: Configure your app in Adjust to enable deep linking. -slug: en/sdk/ios/v4/features/deep-links/deep-link -sidebar-position: 2 -versions: - - label: v5 - value: v5 - default: true - - label: v4 - value: v4 -redirects: - v5: /en/sdk/ios/features/deep-links/deep-link ---- - -You need to configure your app in Adjust to enable deep linking. To do this, make sure you have done the following: - -- [ ] [Added your app in Adjust](https://help.adjust.com/en/article/app-setup). -- [ ] Retrieved all the required deep linking data. - -## Set up universal links in your app {#set-up-universal-links-in-your-app} - -To enable deep linking support for iOS 9 and later you need to set up universal links in Adjust. - - - -You can enter only one Bundle ID per app. If you are testing an app with a Debug Bundle ID, you need to create a separate app. - - - -Once you have gathered your setup data, you can add this to your app in Adjust. Adding the information to your app enables you to add deep links to your campaigns. To set up universal links, follow these steps: - -1. Go to **AppView** and select your app. -2. Ensure the **iOS bundle ID** is present. -3. Under **Device type**, choose your app's default device: - - Universal - iPhone and iPad - - iPhone - - iPad -4. Under **Universal linking**, turn on Enable universal linking to enable universal links for your app. - - Enter the **App ID prefix**. - - Enter the **App scheme**. -5. (Optional) Turn on **Redirect all clicks to a custom URL** and enter a **Custom URL**, if you want your users to go to a custom website instead of the App Store. This is recommended if your app doesn't have an App ID. -6. (Optional) Turn on **Send data to App Store Connect** and enter the **Apple provider ID** to send data to App Store Connect App Analytics. -7. Select **Save**. - -## Set up deep links with a custom URL scheme {#set-up-deep-links-with-a-custom-url-scheme} - -In this case, you need to pick a custom URL scheme name which your app will be responsible for opening. You can then use this scheme name in the Adjust link as part of the deep link parameter. - -To create a deep link with a custom URL scheme, follow these steps: - -1. Define the format of your custom URL scheme. If you are using a cross-platform framework, refer to the documentation for that framework to define the format of your custom URL scheme. Example: `example://summer-clothes?promo=beach` -2. URL encode the deep link. Example: `example%3A%2F%2Fsummer-clothes%3Fpromo%3Dbeach` -3. Pass this encoded deep link into an Adjust link. Example: `https://app.adjust.com/abc123?deeplink=%3A%2F%2Fsummer-clothes%3Fpromo%3Dbeach` -4. Append the `deeplink_js=1` parameter to the link with the encoded deep link. This forces the Adjust system to use the iOS custom URL scheme. Example: `https://app.adjust.com/abc123?deeplink_js=1&deeplink=%3A%2F%2Fsummer-clothes%3Fpromo%3Dbeach` diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/deferred.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/deferred.mdx deleted file mode 100644 index c8713a3e5..000000000 --- a/src/content/docs/sdk/ios/v4/features/deep-links/deferred.mdx +++ /dev/null @@ -1,212 +0,0 @@ ---- -title: Set up deferred deep linking -description: Configure deferred deep linking for your app. -slug: en/sdk/ios/v4/features/deep-links/deferred -sidebar-position: 4 -versions: - - label: v5 - value: v5 - default: true - - label: v4 - value: v4 -redirects: - v5: /en/sdk/ios/features/deep-links/deferred ---- - -A deferred deep link sends a user to a place in your app after routing them via the App Store to install the app first. - -## How it works {#how-it-works} - -This is how a deferred deep link works: - -1. The user clicks on an Adjust deep link. -2. Adjust's servers redirect the user to the App Store. -3. The user installs your app and opens it. -4. Adjust's servers perform attribution and send the deep link to the Adjust SDK. -5. Your app displays its preliminary content, such as onboarding screens and user login, if applicable. -6. Your app retrieves the deep link from the Adjust SDK and handles the deep link. - -## Setup {#setup} - - - -When you set up Adjust deferred deep linking, you need to disable deferred deep linking in other SDKs in your app. You also need to disable any deep linking setup from other MMPs. - - - - - -Check out the [Facebook deferred deep linking documentation](https://developers.facebook.com/docs/ios/deep-linking#deferred-deep-linking) for information about how to configure deep linking with your Facebook campaigns. - - - -There are 2 approaches to set up deferred deep linking in your app: - -1. Adjust's servers automatically pass the deferred deep link to the Adjust SDK. When the user opens your app, the Adjust SDK automatically calls the `open(_:options:completionHandler:)` method with the deep link. If your app doesn't have preliminary content (for example: onboarding screens and user login), or if your app already handles this content before opening the deep link, then no further configuration is required, and you can skip the rest of this section. -2. If your app has preliminary content (for example: onboarding screens and user login), but your app doesn't already handle this before handling the deep link, then you can add a deferred deep link listener. - -### Set up a deferred deep link listener {#set-up-a-deferred-deep-link-listener} - -1. Set up a delegate callback for deferred deep linking. If you have already configured attribution callbacks, you can skip this step. - - - - - - -```swift -class AppDelegate: UIResponder, UIApplicationDelegate, AdjustDelegate { -} -``` - - - - - - - - -```objc -@interface AppDelegate : UIResponder -``` - - - - - - -2. If you haven’t already done so, create an instance of the `ADJConfig` class and set a delegate on the `ADJConfig` object in your app delegate. You need to set the delegate in `ADJConfig` before initializing the SDK. - - - - -```swift -let yourAppToken = "{YourAppToken}" -let environment = ADJEnvironmentSandbox as? String -let adjustConfig = ADJConfig( - appToken: yourAppToken, - environment: environment) -adjustConfig?.delegate = self - -// ... - -Adjust.appDidLaunch(adjustConfig) -``` - - - - -```objc -*adjustConfig = [ADJConfig configWithAppToken:@"{YourAppToken}" - environment:ADJEnvironmentSandbox]; -[adjustConfig setDelegate:self]; - -// ... - -[Adjust appDidLaunch:adjustConfig]; -``` - - - - -3. Add the `adjustDeeplinkResponse` deferred deep link callback method to the delegate. The Adjust SDK calls this method after receiving a deferred deep link. - 1. Set your deep link handling code. - 2. Set the return value of the `adjustDeeplinkResponse` method to true or false. This indicates whether you want the Adjust SDK to call the `open(_:options:completionHandler:)` method to open the deep link after your deep link handling code runs. - - - - -```swift -func adjustDeeplinkResponse(_ deeplink: URL?) -> Bool { - // add your code below to handle deep link - // (for example, show onboarding screens, then open deep link content) - // deeplink object contains the deep link - - return false -} -``` - - - - -```objc -- (BOOL)adjustDeeplinkResponse:(NSURL *)deeplink { - // add your code below to handle deep link - // (for example, show onboarding screens, then open deep link content) - // deeplink object contains the deep link - - return NO; -} -``` - - - - -## Set up Adjust LinkMe {#set-up-adjust-linkme} - - - -```objc -@property (nonatomic, assign) BOOL linkMeEnabled; -``` - - - - - -Discuss with your marketing team whether you need to implement LinkMe in your app. - - - -[Adjust’s LinkMe solution](https://help.adjust.com/en/article/linkme) is an optional feature that ensures robust deferred deep linking performance by enabling your app to read deep link information from the device pasteboard. - - - -The Adjust SDK checks the pasteboard when a user opens the app for the first time. The device displays a dialog asking if the user wants to allow the app to read the pasteboard. - - - -When a user clicks on a LinkMe URL they have the option to copy the link information to their system pasteboard. You can use the Adjust SDK to read the system pasteboard for deep link information. If deep link information is present, the Adjust SDK forwards the user to the correct page in your app. - -To enable pasteboard checking in your app, pass a `true` value to the `setLinkMeEnabled` method on your `ADJConfig` object: - - - - -```swift -let yourAppToken = "{YourAppToken}" -let environment = ADJEnvironmentSandbox as? String -let adjustConfig = ADJConfig( - appToken: yourAppToken, - environment: environment) -// ... -adjustConfig?.linkMeEnabled = true -``` - - - - -```objc -NSString *yourAppToken = @"{YourAppToken}"; -NSString *environment = ADJEnvironmentSandbox; -*adjustConfig = [ADJConfig configWithAppToken:yourAppToken - environment:environment]; -/// ... -[adjustConfig setLinkMeEnabled:YES]; -``` - - - - -```js -setupWebViewJavascriptBridge(function (bridge) { - // ... - var yourAppToken = yourAppToken; - var environment = AdjustConfig.EnvironmentSandbox; - var adjustConfig = new AdjustConfig(yourAppToken, environment); - adjustConfig.setLinkMeEnabled(true); -}); -``` - - - diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/direct.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/direct.mdx deleted file mode 100644 index ec45ba9a5..000000000 --- a/src/content/docs/sdk/ios/v4/features/deep-links/direct.mdx +++ /dev/null @@ -1,392 +0,0 @@ ---- -title: Set up direct deep linking -description: Configure direct deep linking for your app. -slug: en/sdk/ios/v4/features/deep-links/direct -sidebar-position: 3 -versions: - - label: v5 - value: v5 - default: true - - label: v4 - value: v4 -redirects: - v5: /en/sdk/ios/features/deep-links/direct ---- - -You can configure deep linking in your app after you have set it up in the Adjust dashboard. **Direct deep linking** occurs when a user has your app installed on their device. The link takes the user to a specific page within your app. - -To enable deep linking, you need to do the following: - -- [ ] Enable Associated Domains for your app -- [ ] Configure your deep links in Xcode - -## Enable Associated Domains {#enable-associated-domains} - -To get started, you need to enable Associated Domains in your Apple Developer account. This allows you to set universal link domains in your app. To do this, follow these steps: - -1. Log in to your [Apple Developer account](https://developer.apple.com/account/). -2. Select **Certificates, IDs & Profiles** in the left-hand menu. -3. Select **Identifiers** in the left-hand menu. -4. Find your app and select it to open the edit page. -5. Ensure that **Associated Domains** is checked under **Capabilities**. -6. Select **Save** to save your changes. - -## Configure deep links in Xcode {#configure-deep-links-in-xcode} - -Follow these steps to add your deep link configuration to your Xcode project. - -### Adjust universal links domain {#adjust-universal-links-domain} - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Signing & Capabilities** from the top menu. -5. Ensure that **All** is selected in the submenu below. -6. Select the Add option (**+**) to add a capability. -7. Select **Associated Domains**. -8. Enter the Adjust Universal Link domain with the prefix `applinks:` - - Here is an example using the `example.adj.st` domain: `applinks:example.adj.st`. - -### Custom URL scheme {#custom-url-scheme} - - - -Check with your marketing team to see if a custom URL scheme is needed for the app and discuss what the scheme name should be. If your app targets Android devices as well, use the same scheme name for each platform. - - - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Info** from the top menu. -5. Expand the **URL Types** section. -6. Select the Add option (**+**) to add a URL type. -7. Fill in the following information to create a URL scheme: - - **Identifier**: `$(PRODUCT_BUNDLE_IDENTIFIER)` - - **URL Schemes**: Your custom URL scheme. This must be - unique. Don't use protected schemes such as `http`, `https`, or `mailto`. - - **Role**: Editor - -This scheme will work for your production **and** debug builds. - -## Modify your iOS app {#modify-your-ios-app} - -You need to update your iOS app to set up different deep linking scenarios. How you update your app depends on whether your app uses [scenes](https://developer.apple.com/documentation/uikit/app_and_environment/scenes). - -### App doesn't use scenes {#app-doesn-t-use-scenes} - -If your app doesn't uses scenes, you need to update methods in your app delegate. - -#### Universal links {#universal-links} - -Update the `application(_:continue:restorationHandler:)` method in your app delegate to call the following methods in the Adjust SDK: - -- `ADJLinkResolution.resolveLink`: Call this method only if your marketing team requires the use of Adjust's Link Resolution solution. If the deep link uses a domain that matches an element in the `resolveUrlSuffixArray`, then the method attempts to resolve the deep link, and returns the resolved link. If the deep link doesn't match an element in this array, then the method passes through the original deep link, so you can pass all deep links to this method. -- `Adjust.appWillOpen` - Call this method to send deep links to Adjust's servers to record information. You can pass both Adjust and non-Adjust deep links to this method. Adjust's servers will ignore any deep links that don’t have Adjust parameters. - -When a user clicks on your universal link, iOS opens your app and delivers the deep link to `application(_:continue:restorationHandler:)`. This occurs whether the user has closed your app or has it running in the background. - - - - -```swift -func application( - _ application: UIApplication, - continue userActivity: NSUserActivity, - restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) - -> Bool { - - if userActivity.activityType == NSUserActivityTypeBrowsingWeb { - let incomingURL = userActivity.webpageURL - - // call the below method to resolve deep link - ADJLinkResolution.resolveLink( - withUrl: incomingURL, - resolveUrlSuffixArray: ["email.example.com", "short.example.com"], - callback: { resolvedURL in - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - Adjust.appWillOpen(resolvedURL) - }) - } else { - return false - } - - return true -} -``` - - - - -```objc -- (BOOL)application:(UIApplication *)application - continueUserActivity:(NSUserActivity *)userActivity - restorationHandler: - (void (^)(NSArray *restorableObjects))restorationHandler { - - if ([[userActivity activityType] - isEqualToString:NSUserActivityTypeBrowsingWeb]) { - NSURL *incomingURL = [userActivity webpageURL]; - - // call the below method to resolve deep link - [ADJLinkResolution - resolveLinkWithUrl:incomingURL - resolveUrlSuffixArray:@[ - @"email.example.com", @"short.example.com" - ] - callback:^(NSURL* _Nullable resolvedURL) { - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - [Adjust appWillOpenUrl:resolvedURL]; - }]; - } else { - return NO; - } - - return YES; -} -``` - - - - -#### Custom URL scheme {#custom-url-scheme-1} - -If your marketing team requires you to set up custom URL scheme deep links, update the `application(_:open:options:)` method in your app delegate to call the `Adjust.appWillOpen` method in the Adjust SDK. This method sends deep links to Adjust's servers to record them. You can pass both Adjust and non-Adjust deep links to this method. Adjust's servers ignore any deep links that don’t have Adjust parameters. - -When a user clicks on your custom URL scheme deep link, iOS opens your app and delivers the deep link to `application(_:open:options:)`. This occurs whether the user has closed your app or has it running in the background. - - - - -```swift -func application( - _ app: UIApplication, - open incomingURL: URL, - options: [UIApplication.OpenURLOptionsKey: Any] = [:] - ) -> Bool { - - // add your code below to handle deep link - // (for example, open deep link content) - // incomingURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - Adjust.appWillOpen(incomingURL) - - return true -} -``` - - - - -```objc -- (BOOL)application:(UIApplication *)app - openURL:(NSURL *)incomingURL - options:(NSDictionary *)options { - - // add your code below to handle deep link - // (for example, open deep link content) - // incomingURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - [Adjust appWillOpenUrl:incomingURL]; - - return YES; -} -``` - - - - -### App uses scenes {#app-uses-scenes} - -If your app uses scenes, you need to update methods in your scene delegate. - -#### Universal links {#universal-links-1} - -1. Update the `scene(_:willConnectTo:options:)` method in your scene delegate. When a user clicks on your universal links and the user has your app closed, iOS opens your app and delivers the deep link to this method. -2. Update the `scene(_:continue:)` method in your scene delegate. When a user clicks on your universal links, and the user has your app running in the background, iOS opens your app and delivers the deep link to this method. - -The above methods call the following methods in the Adjust SDK: - -- `ADJLinkResolution.resolveLink`: Call this method only if your marketing team requires the use of Adjust's Link Resolution solution. If the deep link uses a domain that matches an element in the `resolveUrlSuffixArray`, then the method attempts to resolve the deep link, and returns the resolved link. If the deep link doesn't match an element in this array, then the method passes through the original deep link, so you can pass all deep links to this method. -- `Adjust.appWillOpen` - Call this method to send deep links to Adjust's servers to record them. You can pass both Adjust and non-Adjust deep links to this method. Adjust's servers ignore any deep links that don’t have Adjust parameters. - - - - -```swift -func scene( - _ scene: UIScene, - willConnectTo session: UISceneSession, - options connectionOptions: UIScene.ConnectionOptions - ) { - - guard let userActivity = connectionOptions.userActivities.first, - userActivity.activityType == NSUserActivityTypeBrowsingWeb, - let incomingURL = userActivity.webpageURL - else { return } - - // call the below method to resolve deep link - ADJLinkResolution.resolveLink( - withUrl: incomingURL, - resolveUrlSuffixArray: ["email.example.com", "short.example.com"], - callback: { resolvedURL in - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - Adjust.appWillOpen(resolvedURL) - }) -} - -func scene( - _ scene: UIScene, - continue userActivity: NSUserActivity) { - - if userActivity.activityType == NSUserActivityTypeBrowsingWeb { - let incomingURL = userActivity.webpageURL - - // call the below method to resolve deep link - ADJLinkResolution.resolveLink( - withUrl: incomingURL, - resolveUrlSuffixArray: ["email.example.com", "short.example.com"], - callback: { resolvedURL in - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - Adjust.appWillOpen(resolvedURL) - }) - } -} -``` - - - - -```objc -- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session - options:(UISceneConnectionOptions *)connectionOptions { - - NSUserActivity* userActivity = - [[[connectionOptions userActivities] allObjects] firstObject]; - - if ([[userActivity activityType] - isEqualToString:NSUserActivityTypeBrowsingWeb]) { - NSURL *incomingURL = [userActivity webpageURL]; - - // call the below method to resolve deep link - [ADJLinkResolution - resolveLinkWithUrl:incomingURL - resolveUrlSuffixArray:@[ - @"email.example.com", @"short.example.com" - ] - callback:^(NSURL* _Nullable resolvedURL) { - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - [Adjust appWillOpenUrl:resolvedURL]; - }]; - } -} - -- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity{ - if ([[userActivity activityType] - isEqualToString:NSUserActivityTypeBrowsingWeb]) { - NSURL *incomingURL = [userActivity webpageURL]; - - // call the below method to resolve deep link - [ADJLinkResolution - resolveLinkWithUrl:incomingURL - resolveUrlSuffixArray:@[ - @"email.example.com", @"short.example.com" - ] - callback:^(NSURL* _Nullable resolvedURL) { - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - [Adjust appWillOpenUrl:resolvedURL]; - }]; - } -} -``` - - - - -#### Custom URL scheme {#custom-url-scheme-2} - -1. Update the `scene(_:willConnectTo:options:)` method in your scene delegate. When a user clicks on your custom URL scheme deep link and the user has your app closed, iOS opens your app and delivers the deep link to this method. -2. Update the `scene(_:openURLContexts:)` method in your scene delegate. When a user clicks on your custom URL scheme deep link, and the user has your app running in the background, iOS opens your app and delivers the deep link to this method. - -These methods call the `Adjust.appWillOpen` method in the Adjust SDK. This method sends deep links to Adjust's servers to recordd them. You can pass both Adjust and non-Adjust deep links to this method. Adjust's servers ignore any deep links that don’t have Adjust parameters. - - - - -```swift -func scene( - _ scene: UIScene, - openURLContexts URLContexts: Set - ) { - - guard let incomingURL = URLContexts.first?.url else { - return - } - - // add your code below to handle deep link - // (for example, open deep link content) - // incomingURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - Adjust.appWillOpen(incomingURL) -} -``` - - - - -```objc -- (void)scene:(UIScene *)scene - openURLContexts:(nonnull NSSet *)URLContexts { - - NSURL *incomingURL = [[URLContexts allObjects] firstObject].URL; - - if (incomingURL) { - // add your code below to handle deep link - // (for example, open deep link content) - // incomingURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - [Adjust appWillOpenUrl:incomingURL]; - } -} -``` - - - diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/index.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/index.mdx index 5e417e2fe..33ad6652b 100644 --- a/src/content/docs/sdk/ios/v4/features/deep-links/index.mdx +++ b/src/content/docs/sdk/ios/v4/features/deep-links/index.mdx @@ -13,10 +13,3 @@ versions: redirects: v5: /en/sdk/ios/features/deep-links --- - -You can create deep links to take users to specific pages in your app. The Adjust SDK uses different logic depending on if the user already has your app installed on their device: - -- Direct deep linking: occurs if the user already has your app installed. The link takes the user to the page specified in the link -- Deferred deep linking: occurs if the user doesn't have your app installed. The link takes the user to a storefront to install your app first. After the user installs the app, it opens to the page specified in the link. - -The SDK can read deep link data after a user opens your app from a link. Follow the steps in this section to get started. diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/resolution.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/resolution.mdx index f8dda7614..f5227fdc1 100644 --- a/src/content/docs/sdk/ios/v4/features/deep-links/resolution.mdx +++ b/src/content/docs/sdk/ios/v4/features/deep-links/resolution.mdx @@ -1,10 +1,8 @@ --- title: Link resolution -description: - Set up link resolution for deep linking via email, SMS, QR codes, and - platforms that shorten links. +description: Set up link resolution for deep linking via email, SMS, QR codes, and platforms that shorten links. slug: en/sdk/ios/v4/features/deep-links/resolution -sidebar-position: 5 +sidebar-position: 3 versions: - label: v5 value: v5 diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/set-up-deep-linking.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/set-up-deep-linking.mdx new file mode 100644 index 000000000..f25f75ec4 --- /dev/null +++ b/src/content/docs/sdk/ios/v4/features/deep-links/set-up-deep-linking.mdx @@ -0,0 +1,975 @@ +--- +title: Set up deep linking +description: Set up deep linking in your app. +slug: en/sdk/ios/v4/features/deep-links/set-up-deep-linking +sidebar-position: 2 +versions: + - label: v5 + value: v5 + default: true + - label: v4 + value: v4 +redirects: + v5: /en/sdk/ios/features/deep-links/set-up-deep-linking +--- + +Please follow the steps in the section that corresponds to the type of app you have: + +- [UIKit apps using AppDelegate lifecycle](#uikit-apps-using-appdelegate-lifecycle) +- [UIKit apps using SceneDelegate lifecycle](#uikit-apps-using-scenedelegate-lifecycle) +- [SwiftUI apps using AppDelegate lifecycle](#swiftui-apps-using-appdelegate-lifecycle) +- [SwiftUI apps using SceneDelegate lifecycle](#swiftui-apps-using-scenedelegate-lifecycle) + +In addition, implement deep link handling logic: + +- [Deep link handler](#deep-link-handler) + +## UIKit apps using AppDelegate lifecycle + +Update your AppDelegate to implement direct and deferred deep linking. + + + + + + +```swift +import Adjust +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate, + AdjustDelegate +{ + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: + [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + // Configure Adjust SDK + // Replace {YourAppToken} with your Adjust app token + let appToken = "{YourAppToken}" + var adjustConfig: ADJConfig? + + #if DEBUG + adjustConfig = ADJConfig( + appToken: appToken, + environment: ADJEnvironmentSandbox) + adjustConfig?.logLevel = ADJLogLevelVerbose + #else + adjustConfig = ADJConfig( + appToken: appToken, + environment: ADJEnvironmentProduction, + allowSuppressLogLevel: true) + adjustConfig?.logLevel = ADJLogLevelSuppress + #endif + + // Wait up to 120 seconds after app open for user to respond to ATT + // before sending install session to Adjust's servers. + // Ensure this interval is long enough for user to respond. + adjustConfig?.attConsentWaitingInterval = 120 + + // Create delegate for deferred deep linking + adjustConfig?.delegate = self + + // Initialize Adjust SDK + Adjust.appDidLaunch(adjustConfig) + + return true + } + + // Receive universal link when app is installed, + // and app is in "not running" or background state, + // and user clicks link or another app opens link using + // UIApplication.open(_:options:completionHandler:). + func application( + _ application: UIApplication, + continue userActivity: NSUserActivity, + restorationHandler: + @escaping ([UIUserActivityRestoring]?) -> Void + ) -> Bool { + if userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingLink = userActivity.webpageURL + { + // Handle incoming universal link + DeeplinkHandler.handleDeeplink(incomingLink) + } + return true + } + + // Receive app scheme deep link when app is installed, and either: + // - App is in "not running" or background state, and user clicks link or + // another app opens link using UIApplication.open(_:options:completionHandler:), or + // - App is in foreground state and opens its own link using + // UIApplication.open(_:options:completionHandler:). + func application( + _ application: UIApplication, + open incomingLink: URL, + options: [UIApplication.OpenURLOptionsKey: Any] = [:] + ) -> Bool { + // Handle incoming app scheme deep link + DeeplinkHandler.handleDeeplink(incomingLink) + return true + } + + // Receive deferred deep link via AdjustDelegate method + func adjustDeeplinkResponse(_ deeplink: URL?) -> Bool { + if let incomingLink = deeplink { + if UserDefaults.standard.bool(forKey: "HasCompletedOnboarding") { + // If onboarding is complete, handle deferred deep link immediately + DeeplinkHandler.handleDeeplink(incomingLink) + } else { + // Store deferred deep link to invoke after onboarding screens and login + UserDefaults.standard.set(incomingLink.absoluteString, forKey: "lastDeferredLink") + } + } + // Return true to let Adjust SDK attempt to open deep link immediately + // upon receipt (for example: app has no ATT, onboarding screens, or login). + // Otherwise, return false to prevent SDK from opening the deep link immediately. + return false + } +} +``` + + + + + + + + +```objc +#import +#import "AppDelegate.h" +#import "DeeplinkHandler.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Configure Adjust SDK + // Replace {YourAppToken} with your Adjust app token + NSString *appToken = @"{YourAppToken}"; + ADJConfig *adjustConfig; + +#ifdef DEBUG + adjustConfig = [ADJConfig configWithAppToken:appToken + environment:ADJEnvironmentSandbox]; + [adjustConfig setLogLevel:ADJLogLevelVerbose]; +#else + adjustConfig = [ADJConfig configWithAppToken:appToken + environment:ADJEnvironmentProduction + allowSuppressLogLevel:YES]; + [adjustConfig setLogLevel:ADJLogLevelSuppress]; +#endif + + // Wait up to 120 seconds after app open for user to respond to ATT + // before sending install session to Adjust's servers. + // Ensure this interval is long enough for user to respond. + adjustConfig.attConsentWaitingInterval = 120; + + // Create delegate for deferred deep linking + adjustConfig.delegate = self; + + // Initialize Adjust SDK + [Adjust appDidLaunch:adjustConfig]; + + return YES; +} + +// Receive universal link when app is installed, +// and app is in "not running" or background state, +// and user clicks link or another app opens link using +// [UIApplication openURL:options:completionHandler:]. +- (BOOL)application:(UIApplication *)application + continueUserActivity:(NSUserActivity *)userActivity + restorationHandler: + (void (^)(NSArray> *_Nullable)) + restorationHandler { + if ([userActivity.activityType + isEqualToString:NSUserActivityTypeBrowsingWeb]) { + NSURL *incomingLink = userActivity.webpageURL; + if (incomingLink) { + // Handle incoming universal link + [DeeplinkHandler handleDeeplink:incomingLink]; + } + } + return YES; +} + +// Receive app scheme deep link when app is installed, and either: +// - App is in "not running" or background state, and user clicks link or +// another app opens link using [UIApplication +// openURL:options:completionHandler:], or +// - App is in foreground state and opens its own link using +// [UIApplication openURL:options:completionHandler:]. +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)incomingLink + options: + (NSDictionary *)options { + // Handle incoming app scheme deep link + [DeeplinkHandler handleDeeplink:incomingLink]; + return YES; +} + +// Receive deferred deep link via AdjustDelegate method +- (BOOL)adjustDeeplinkResponse:(NSURL *)deeplink { + if (deeplink) { + if ([[NSUserDefaults standardUserDefaults] + boolForKey:@"HasCompletedOnboarding"]) { + // If onboarding is complete, handle deferred deep link immediately + [DeeplinkHandler handleDeeplink:deeplink]; + } else { + // Store deferred deep link to invoke after onboarding screens and login + [[NSUserDefaults standardUserDefaults] + setObject:deeplink.absoluteString + forKey:@"lastDeferredLink"]; + } + } + // Return YES to let Adjust SDK attempt to open deep link immediately + // upon receipt (for example: app has no ATT, onboarding screens, or login). + // Otherwise, return NO to prevent SDK from opening the deep link immediately. + return NO; +} + +@end +``` + + + + + + +Most apps have some kind of onboarding process (for example: ATT prompt, onboarding screens, login prompt). When setting up deferred deep linking, you have to ensure that onboarding and launching deferred deep links don't interfere with each other. One approach is to wait until after onboarding to launch deferred deep links. Here's how it works in the code examples: + +1. A user who doesn't have the app installed clicks an Adjust deep link, which redirects them to the app store. +2. The user installs and opens the app. +3. The app begins its onboarding process. +4. After the ATT prompt response or timeout, the Adjust SDK sends session and attribution requests to Adjust's servers. +5. Adjust's servers respond with attribution data, including the deep link the user clicked on ("deferred deep link"). +6. The Adjust SDK triggers a deferred deep link callback in the app (shown in the AppDelegate implementation above). The callback checks whether onboarding is complete: + - If onboarding is complete, it handles the deep link immediately. + - If onboarding isn't complete, it stores the deep link. +7. Once onboarding completes, the app checks for and handles any stored deferred deep link (shown in the ViewController example class below). +8. The app navigates the user to the deep link screen. + + + + + + +```swift +import Adjust +import UIKit + +class ViewController: UIViewController { + var hasCompletedOnboarding: Bool { + get { + UserDefaults.standard.bool(forKey: "HasCompletedOnboarding") + } + set { + UserDefaults.standard.set(newValue, forKey: "HasCompletedOnboarding") + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + // Check if onboarding has been completed + if !hasCompletedOnboarding { + // Show onboarding screens and login prompt + + // Show ATT prompt after delay to ensure app is active + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + Adjust.requestTrackingAuthorization { _ in } + } + + // After onboarding, set hasCompletedOnboarding to true + hasCompletedOnboarding = true + + // Check for deferred deep link that arrived during onboarding + if let deferredLinkString = UserDefaults.standard.string( + forKey: "lastDeferredLink" + ), + let deferredLink = URL(string: deferredLinkString) { + // Remove stored deferred deep link to avoid handling again later + UserDefaults.standard.removeObject(forKey: "lastDeferredLink") + // Handle deferred deep link + DeeplinkHandler.handleDeeplink(deferredLink) + } + } else { + // Show main content + } + } +} +``` + + + + + + + + +```objc +#import +#import "DeeplinkHandler.h" +#import "ViewController.h" + +@implementation ViewController + +- (BOOL)hasCompletedOnboarding { + return [[NSUserDefaults standardUserDefaults] + boolForKey:@"HasCompletedOnboarding"]; +} + +- (void)setHasCompletedOnboarding:(BOOL)hasCompletedOnboarding { + [[NSUserDefaults standardUserDefaults] setBool:hasCompletedOnboarding + forKey:@"HasCompletedOnboarding"]; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + // Check if onboarding has been completed + if (!self.hasCompletedOnboarding) { + // Show onboarding screens and login prompt + // Show ATT prompt after delay to ensure app is active + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [Adjust requestTrackingAuthorization]; + }); + + // After onboarding, set hasCompletedOnboarding to true + self.hasCompletedOnboarding = YES; + + // Check for deferred deep link that arrived during onboarding + NSString *deferredLinkString = + [[NSUserDefaults standardUserDefaults] stringForKey:@"lastDeferredLink"]; + NSURL *deferredLink = [NSURL URLWithString:deferredLinkString]; + if (deferredLink) { + // Remove stored deferred deep link to avoid handling again later + [[NSUserDefaults standardUserDefaults] + removeObjectForKey:@"lastDeferredLink"]; + // Handle deferred deep link + [DeeplinkHandler handleDeeplink:deferredLink]; + } + } else { + // Show main content + } +} + +@end +``` + + + + + + +Lastly, implement your deep link handling logic: + +[Deep link handler](#deep-link-handler) + +## UIKit apps using SceneDelegate lifecycle + +Follow the steps in the [UIKit apps using AppDelegate lifecycle section](#uikit-apps-using-appdelegate-lifecycle), except instead of implementing the `application(_:continue:restorationHandler:)` and `application(_:open:options:)` methods in your AppDelegate for direct deep linking, implement the following methods in your SceneDelegate. + + + + + + +```swift +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + // Receive universal link or app scheme deep link + // when app is installed, and app is in "not running" state, and either: + // - User clicks link, or + // - Another app opens link using + // UIApplication.open(_:options:completionHandler:). + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + // Handle incoming universal link + if let userActivity = connectionOptions.userActivities.first, + userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingLink = userActivity.webpageURL + { + DeeplinkHandler.handleDeeplink(incomingLink) + } + + // Handle incoming app scheme deep link + else if let urlContext = connectionOptions.urlContexts.first { + let incomingLink = urlContext.url + DeeplinkHandler.handleDeeplink(incomingLink) + } + } + + // Receive universal link when app is installed, + // and app is in background state, and either: + // - User clicks link, or + // - Another app opens link using + // UIApplication.open(_:options:completionHandler:). + func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + if userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingLink = userActivity.webpageURL + { + // Handle incoming universal link + DeeplinkHandler.handleDeeplink(incomingLink) + } + } + + // Receive app scheme deep link when app is installed, and either: + // - App is in background state, and user clicks link or another app opens link using + // UIApplication.open(_:options:completionHandler:), or + // - App is in foreground and opens its own link using + // UIApplication.open(_:options:completionHandler:). + func scene( + _ scene: UIScene, openURLContexts URLContexts: Set + ) { + if let urlContext = URLContexts.first { + let incomingLink = urlContext.url + + // Handle incoming app scheme deep link + DeeplinkHandler.handleDeeplink(incomingLink) + } + } +} +``` + + + + + + + + +```objc +#import "DeeplinkHandler.h" +#import "SceneDelegate.h" + +@implementation SceneDelegate + +// Receive universal link or app scheme deep link +// when app is installed, and app is in "not running" state, and either: +// - User clicks link, or +// - Another app opens link using +// [UIApplication openURL:options:completionHandler:]. +- (void)scene:(UIScene *)scene + willConnectToSession:(UISceneSession *)session + options:(UISceneConnectionOptions *)connectionOptions { + // Handle incoming universal link + if (connectionOptions.userActivities.count > 0) { + NSUserActivity *userActivity = + connectionOptions.userActivities.allObjects.firstObject; + if ([userActivity.activityType + isEqualToString:NSUserActivityTypeBrowsingWeb] && + userActivity.webpageURL) { + NSURL *incomingLink = userActivity.webpageURL; + [DeeplinkHandler handleDeeplink:incomingLink]; + } + } + // Handle incoming app scheme deep link + else if (connectionOptions.URLContexts.count > 0) { + UIOpenURLContext *urlContext = + connectionOptions.URLContexts.allObjects.firstObject; + NSURL *incomingLink = urlContext.URL; + [DeeplinkHandler handleDeeplink:incomingLink]; + } +} + +// Receive universal link when app is installed, +// and app is in background state, and either: +// - User clicks link, or +// - Another app opens link using +// [UIApplication openURL:options:completionHandler:]. +- (void)scene:(UIScene *)scene + continueUserActivity:(NSUserActivity *)userActivity { + if ([userActivity.activityType + isEqualToString:NSUserActivityTypeBrowsingWeb]) { + NSURL *incomingLink = userActivity.webpageURL; + + // Handle incoming universal link + [DeeplinkHandler handleDeeplink:incomingLink]; + } +} + +// Receive app scheme deep link when app is installed, and either: +// - App is in background state, and user clicks link or another app opens link +// using [UIApplication openURL:options:completionHandler:], or +// - App is in foreground and opens its own link using +// [UIApplication openURL:options:completionHandler:]. +- (void)scene:(UIScene *)scene + openURLContexts:(NSSet *)URLContexts { + UIOpenURLContext *urlContext = URLContexts.allObjects.firstObject; + if (urlContext) { + NSURL *incomingLink = urlContext.URL; + + // Handle incoming app scheme deep link + [DeeplinkHandler handleDeeplink:incomingLink]; + } +} + +@end +``` + + + + + + +## SwiftUI apps using AppDelegate lifecycle + +If you haven't already done so, create an `AppDelegate.swift` file in your project's main directory and reference it in your main application file (for example: `App.swift` as shown below). This is required to handle app lifecycle events and Adjust SDK integration. Also implement `onOpenURL`, which receives all direct deep links. + + + + + + +```swift +import SwiftUI + +@main +struct MyApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + var body: some Scene { + WindowGroup { + ContentView() + // Receive universal link or app scheme deep link + // when app is installed, and app is in "not running" or background state, and either: + // - User clicks link, or + // - Another app opens link using + // UIApplication.open(_:options:completionHandler:) or SwiftUI's openURL. + // + // Receive app scheme deep link when app is installed, + // and app is in foreground and opens its own link using + // UIApplication.open(_:options:completionHandler:) or SwiftUI's openURL. + .onOpenURL { incomingLink in + DeeplinkHandler.handleDeeplink(incomingLink) + } + } + } +} +``` + + + + + + +Update your AppDelegate to implement deferred deep linking. + + + + + + +```swift +import Adjust +import UIKit + +class AppDelegate: UIResponder, UIApplicationDelegate, AdjustDelegate { + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: + [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + // Configure Adjust SDK + // Replace {YourAppToken} with your Adjust app token + let appToken = "{YourAppToken}" + var adjustConfig: ADJConfig? + + #if DEBUG + adjustConfig = ADJConfig( + appToken: appToken, + environment: ADJEnvironmentSandbox) + adjustConfig?.logLevel = ADJLogLevelVerbose + #else + adjustConfig = ADJConfig( + appToken: appToken, + environment: ADJEnvironmentProduction, + allowSuppressLogLevel: true) + adjustConfig?.logLevel = ADJLogLevelSuppress + #endif + + // Wait up to 120 seconds after app open for user to respond to ATT + // before sending install session to Adjust's servers. + // Ensure this interval is long enough for user to respond. + adjustConfig?.attConsentWaitingInterval = 120 + + // Create delegate for deferred deep linking + adjustConfig?.delegate = self + + // Initialize Adjust SDK + Adjust.appDidLaunch(adjustConfig) + + return true + } + + // Receive deferred deep link via AdjustDelegate method + func adjustDeeplinkResponse(_ deeplink: URL?) -> Bool { + if let incomingLink = deeplink { + if UserDefaults.standard.bool(forKey: "HasCompletedOnboarding") { + // If onboarding is complete, handle deferred deep link immediately + DeeplinkHandler.handleDeeplink(incomingLink) + } else { + // Store deferred deep link to invoke after onboarding screens and login + UserDefaults.standard.set(incomingLink.absoluteString, forKey: "lastDeferredLink") + } + } + // Return true to let Adjust SDK attempt to open deep link immediately + // upon receipt (for example: app has no ATT, onboarding screens, or login). + // Otherwise, return false to prevent SDK from opening the deep link immediately. + return false + } +} +``` + + + + + + +Most apps have some kind of onboarding process (for example: ATT prompt, onboarding screens, login prompt). When setting up deferred deep linking, you have to ensure that onboarding and launching deferred deep links don't interfere with each other. One approach is to wait until after onboarding to launch deferred deep links. Here's how it works in the code examples: + +1. A user who doesn't have the app installed clicks an Adjust deep link, which redirects them to the app store. +2. The user installs and opens the app. +3. The app begins its onboarding process. +4. After the ATT prompt response or timeout, the Adjust SDK sends session and attribution requests to Adjust's servers. +5. Adjust's servers respond with attribution data, including the deep link the user clicked on ("deferred deep link"). +6. The Adjust SDK triggers a deferred deep link callback in the app (shown in the AppDelegate implementation above). The callback checks whether onboarding is complete: + - If onboarding is complete, it handles the deep link immediately. + - If onboarding isn't complete, it stores the deep link. +7. Once onboarding completes, the app checks for and handles any stored deferred deep link (shown in the ContentView example class below). +8. The app navigates the user to the deep link screen. + + + + + + +```swift +import Adjust +import SwiftUI + +struct ContentView: View { + var body: some View { + NavigationStack { + VStack { + // Check if onboarding has been completed + if !UserDefaults.standard.bool(forKey: "HasCompletedOnboarding") { + // Show onboarding screens and login prompt + + // Show ATT prompt after delay to ensure app is active + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + Adjust.requestTrackingAuthorization { _ in } + } + + // After onboarding, set hasCompletedOnboarding to true + UserDefaults.standard.set(true, forKey: "HasCompletedOnboarding") + + // Check for deferred deep link that arrived during onboarding + if let deferredLinkString = UserDefaults.standard.string( + forKey: "lastDeferredLink" + ), + let deferredLink = URL(string: deferredLinkString) { + // Remove stored deferred deep link to avoid handling again later + UserDefaults.standard.removeObject(forKey: "lastDeferredLink") + // Handle deferred deep link + DeeplinkHandler.handleDeeplink(deferredLink) + } + } + } else { + // Show main content + } + } + } + } +} +``` + + + + + + +Lastly, implement your deep link handling logic: + +[Deep link handler](#deep-link-handler) + +## SwiftUI apps using SceneDelegate lifecycle + +Follow the steps in the [SwiftUI apps using AppDelegate lifecycle](#swiftui-apps-using-appdelegate-lifecycle). In addition, implement the following method in your SceneDelegate. + + + + + + +```swift +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + // Receive universal link or app scheme deep link + // when app is installed, and app is in "not running" state, and either: + // - User clicks link, or + // - Another app opens link using + // UIApplication.open(_:options:completionHandler:) or SwiftUI's openURL. + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + // Handle incoming universal link + if let userActivity = connectionOptions.userActivities.first, + userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingLink = userActivity.webpageURL + { + DeeplinkHandler.handleDeeplink(incomingLink) + } + // Handle incoming app scheme deep link + else if let urlContext = connectionOptions.urlContexts.first { + let incomingLink = urlContext.url + DeeplinkHandler.handleDeeplink(incomingLink) + } + } +} +``` + + + + + + +## Deep link handler + +The preceding code examples use an example DeeplinkHandler class. This example class is shown below and handles all types of links: + +- Adjust branded links (full go.link links) +- Adjust short branded links (short go.link links) +- Adjust universal links (adj.st links) +- Non-Adjust universal links (example.com links) +- App scheme deep links (example:// links) + +The class performs the following tasks: + +1. The class uses Adjust's `processDeeplink` method, which sends the deep link to Adjust's servers to accomplish two things: + +- Record the deep link click for attribution purposes. +- If the deep link is an [Adjust short branded link](https://www.help.adjust.com/en/article/short-branded-links), respond with the corresponding full URL. Otherwise, respond with the original link. + +2. After processing, the class parses the link and navigates to the appropriate screen. This part of the code is specific to each app. Your app has to implement its own logic for handling deep links and opening the corresponding content. Your deep link handling has to meet the following key requirements: + +- Your app should treat Adjust branded links the same as other universal links, such as your own. For example, the following links should navigate to the same screen in your app: + - Adjust branded link: `https://example.go.link/summer-clothes?promo=beach` + - Your universal link: `https://www.example.com/summer-clothes?promo=beach` +- In cases where iOS doesn't support universal links, Adjust automatically converts them to app scheme deep links. Additionally, Adjust's servers convert all deferred deep links to app scheme deep link format. Therefore it's crucial for the app to handle universal links and app scheme deep links equivalently. For example, the following links should navigate to the same screen in your app: + - Adjust branded link: `https://example.go.link/summer-clothes?promo=beach` + - App scheme deep link: `example://summer-clothes?promo=beach` + + + + + + +```swift +import Adjust +// import SwiftUI and/or UIKit + +class DeeplinkHandler { + static func handleDeeplink(_ incomingLink: URL) { + // Send incoming deep link to Adjust's servers for attribution + // and retrieve full URL if short branded link. + // If not, retrieve original link. + Adjust.processDeeplink(incomingLink) { processedLinkString in + guard let processedLink = URL(string: processedLinkString) else { return } + + // Extract path, query items, and fragment from the processed link. + guard let components = URLComponents( + url: processedLink, + resolvingAgainstBaseURL: true + ) else { return } + + // For app scheme deep links, set path = host + path + var path: String + if let scheme = processedLink.scheme, scheme == "http" || + scheme == "https" { + path = components.path + } else { + path = (components.host ?? "") + components.path + if !path.isEmpty && !path.hasPrefix("/") { + path = "/" + path + } + } + + let queryItems = components.queryItems ?? [] + // Parse query parameters into a dictionary for easier access + let params = queryItems.reduce(into: [String: String]()) { result, item in + result[item.name] = item.value + } + let fragment = components.fragment + + // Implement the navigation or other app-specific logic based on + // the deep link components. + DispatchQueue.main.async { + // Example of navigating based on the path or other components. + // Replace with your actual navigation logic. + switch path { + case "/product": + if let productId = params["id"] { + handleProduct(productId: productId) + } + case "/category": + if let categoryName = params["name"] { + handleCategory(category: categoryName) + } + case "/search": + if let query = params["q"] { + handleSearch(query: query) + } + default: + print("Unhandled deep link path: \(path)") + } + } + } + } + + private static func handleProduct(productId: String) { + // Example UIKit implementation: + // let productVC = ProductViewController(productId: productId) + // navigationController.pushViewController(productVC, animated: true) + // Example SwiftUI implementation: + // let productView = ProductView(productId: productId) + // navigationPath.append(productView) + // or: router.navigate(to: productView) + } + + private static func handleCategory(category: String) { + // Example UIKit implementation: + // let categoryVC = CategoryViewController(category: category) + // navigationController.pushViewController(categoryVC, animated: true) + // Example SwiftUI implementation: + // let categoryView = CategoryView(category: category) + // navigationPath.append(categoryView) + // or: router.navigate(to: categoryView) + } + + private static func handleSearch(query: String) { + // Example UIKit implementation: + // let searchVC = SearchViewController(searchQuery: query) + // navigationController.pushViewController(searchVC, animated: true) + // Example SwiftUI implementation: + // let searchView = SearchView(searchQuery: query) + // navigationPath.append(searchView) + // or: router.navigate(to: productView) + } +} +``` + + + + + + + + +```objc +#import +#import "DeeplinkHandler.h" + +@implementation DeeplinkHandler + ++ (void)handleDeeplink:(NSURL *)incomingLink { + // Send incoming deep link to Adjust's servers for attribution + // and retrieve full URL if short branded link. + // If not, retrieve original link. + [Adjust processDeeplink:incomingLink + completionHandler:^(NSString *_Nonnull processedLinkString) { + NSURL *processedLink = [NSURL URLWithString:processedLinkString]; + if (!processedLink) return; + + // Extract path, query items, and fragment from the processed link. + NSURLComponents *components = + [NSURLComponents componentsWithURL:processedLink + resolvingAgainstBaseURL:YES]; + + // For app scheme deep links, set path = host + path + NSString *path; + if ([processedLink.scheme isEqualToString:@"http"] || + [processedLink.scheme isEqualToString:@"https"]) { + path = components.path; + } else { + path = [NSString stringWithFormat:@"%@%@", + components.host ?: @"", + components.path]; + if (path.length > 0 && ![path hasPrefix:@"/"]) { + path = [@"/" stringByAppendingString:path]; + } + } + + NSArray *queryItems = components.queryItems ?: @[]; + + // Parse query parameters into a dictionary for easier access + NSMutableDictionary *params = + [NSMutableDictionary dictionary]; + for (NSURLQueryItem *item in queryItems) { + params[item.name] = item.value; + } + + NSString *fragment = components.fragment; + + // Implement the navigation or other app-specific logic based on + // the deep link components. + dispatch_async(dispatch_get_main_queue(), ^{ + // Example of navigating based on the path or other components. + // Replace with your actual navigation logic. + if ([path isEqualToString:@"/product"]) { + NSString *productId = params[@"id"]; + if (productId) { + ProductViewController *productVC = + [[ProductViewController alloc] initWithProductId:productId]; + [self navigateToViewController:productVC]; + } + } else if ([path isEqualToString:@"/category"]) { + NSString *categoryName = params[@"name"]; + if (categoryName) { + CategoryViewController *categoryVC = + [[CategoryViewController alloc] initWithCategory:categoryName]; + [self navigateToViewController:categoryVC]; + } + } else if ([path isEqualToString:@"/search"]) { + NSString *query = params[@"q"]; + if (query) { + SearchViewController *searchVC = + [[SearchViewController alloc] initWithSearchQuery:query]; + [self navigateToViewController:searchVC]; + } + } else { + NSLog(@"Unhandled deep link path: %@", path); + } + }); + }]; +} + ++ (void)navigateToViewController:(UIViewController *)viewController { + // Example navigation implementation: + // UINavigationController *navigationController = [self + // getNavigationController]; [navigationController + // pushViewController:viewController animated:YES]; +} + +@end +``` + + + + + diff --git a/src/content/docs/sdk/ios/v4/features/deep-links/testing.mdx b/src/content/docs/sdk/ios/v4/features/deep-links/testing.mdx index 4d4bf4479..e76b0ecd3 100644 --- a/src/content/docs/sdk/ios/v4/features/deep-links/testing.mdx +++ b/src/content/docs/sdk/ios/v4/features/deep-links/testing.mdx @@ -2,7 +2,7 @@ title: Test deep linking description: Test your deep links to ensure they work as expected. slug: en/sdk/ios/v4/features/deep-links/testing -sidebar-position: 6 +sidebar-position: 4 versions: - label: v5 value: v5 diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/configure-deep-link-settings.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/configure-deep-link-settings.mdx new file mode 100644 index 000000000..15a16e6c9 --- /dev/null +++ b/src/content/docs/sdk/ios/v5/features/deep-links/configure-deep-link-settings.mdx @@ -0,0 +1,110 @@ +--- +title: Configure deep link settings +description: Configure deep link settings for your app +slug: en/sdk/ios/features/deep-links/configure-deep-link-settings +sidebar-position: 1 +versions: + - label: v5 + value: v5 + default: true + - label: v4 + value: v4 +redirects: + v4: /en/sdk/ios/v4/features/deep-links/configure-deep-link-settings +--- + +
+## Create an app + +[Create an app](https://help.adjust.com/en/article/app-setup) in the Adjust dashboard. + +Please note that the Adjust dashboard only supports one bundle ID (`com.example.app`) and app scheme (`example://`) for each app. If your app uses different bundle IDs and/or app schemes for your release and debug builds, create a separate debug app. + +## Add iOS platform + +Add iOS in the platform settings for your app. This step requires entering the bundle ID. If you're not sure of the bundle ID for your app build, enter a temporary value (`com.example.app`) to save the platform settings. Follow the rest of this guide to collect all required data points, then return to platform settings in the dashboard to finish the configuration. + +
+## Set up a branded domain + +In the Adjust dashboard, [set up a branded domain](https://help.adjust.com/en/article/set-up-branded-domain) using Adjust's go.link domain (for example: `brandname.go.link`). + +If your iOS app and Android app use separate apps in Adjust, ensure that you select the same branded domain (`brandname.go.link`) for both apps in the Adjust dashboard. + +If you use separate apps for release and debug builds, create a separate branded domain for your debug app (`brandnamedebug.go.link`). If your debug apps use separate apps for iOS and Android in Adjust, ensure that you also select this same branded domain for both debug apps in the Adjust dashboard. + +Make note of your branded domains to configure in Xcode in the next section. + +
+## Configure settings in Xcode +
+### Configure universal links + +1. Open your Xcode project. +2. In the navigator pane, select the project name to access the project settings. +3. In the project settings, under **Targets**, select the appropriate target (usually your app's name). +4. Select the **Signing & Capabilities** tab. +5. Complete these steps for both the **Release** and **Debug** sub-tabs: + + - Make note of the value in the **Bundle Identifier** field. This is your app's bundle ID ("Release Bundle ID" or "Debug Bundle ID," respectively), which you need to configure in the iOS platform settings in the Adjust dashboard. + - In the **Associated Domains** section, add an entry for each of your branded domains. For the example domain `brandname.go.link`, here is the required entry: + + `applinks:brandname.go.link` + +**Troubleshoot missing or problematic Associated Domains settings** + +- If you don't see the **Associated Domains** section you might need to enable it first: + 1. Next to the **Release** or **Debug** sub-tab (wherever it's missing), click **+ Capability**. + 2. Search for "Associated Domains" and select it. +- If you get an error message, such as "Provisioning profile \{profile_name\} doesn't support the Associated Domains capability" when trying to enable Associated Domains, your provisioning profile likely needs to be updated: + - For automatic signing, make sure "Automatically manage signing" is selected at the top of the Signing & Capabilities page. + - For manual signing: + 1. Go to the Apple Developer portal and [enable the Associated Domains capability](https://developer.apple.com/help/account/manage-identifiers/enable-app-capabilities) for your app. + 2. [Download and import](https://help.apple.com/xcode/mac/current/#/dev1bf96f17e) the updated provisioning profile into Xcode. + +### Retrieve or configure app scheme + +App scheme is required for certain use cases where iOS doesn’t support universal links. You can reuse an existing app scheme for Adjust deep linking. + +1. In the Xcode navigator pane, select the project name to access the project settings. +2. Under Targets, select the appropriate target (usually your app’s name). +3. Select the **Info** tab. +4. Expand the **URL Types** section. + +If your app already has an app scheme, there will be URL Type entries, each of which has a single value for **URL Schemes**. Use the table below to determine your app schemes based on your URL Types configuration. Make note of the app schemes to configure them in the Adjust dashboard later. + +| **URL Schemes** field | App Scheme | +| ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Static value, such as `example` | `example://` is the "Release App Scheme" | +| Static values, such as `example`, `exampleDebug` in separate URL Types entries | `example://` is the "Release App Scheme"
`exampleDebug://` is the "Debug App Scheme" | +| Dynamic build setting variable, such as `$(APP_SCHEME)` | Go to **Build Settings** tab -> search for variable name, such as "APP_SCHEME" -> values for Release and Debug are the "Release App Scheme" and "Debug App Scheme," respectively | + +If you need to create an app scheme, follow these steps: + +- Select the **+** button to add a new URL Type. +- Fill in the following fields: + - **Identifier**: `$(PRODUCT_BUNDLE_IDENTIFIER)` + - **Role**: Editor + - **URL Schemes**: Enter your desired app scheme value (enter `example`, not `example://`). Don't use `http`, `https`, or reserved iOS schemes like `mailto`, `tel`, `sms`, or `facetime`. Entering a static value here will create a single app scheme used for both release and debug builds. +- Once created, make note of the app scheme for configuration in the Adjust dashboard later. + +
+## Retrieve App ID Prefix from Apple Developer Portal + +1. Log into the [Apple Developer portal](https://developer.apple.com/account/). +2. Under **Certificates, IDs & Profiles**, select **Identifiers**. +3. Select your app. +4. Near the top of the page, make note of the **App ID Prefix** to configure in the Adjust dashboard in the next section. +
+ +## Finish configuring iOS platform settings + +In the Adjust dashboard, finish configuring [iOS platform settings](https://help.adjust.com/en/article/platforms-ios-android-amazon-microsoft) for your apps using the below data points that you collected. + +| Data Point | Example | Requirement | +| ------------------ | ----------------- | ------------------------------------------------------------------ | +| Release Bundle ID | com.example.app | Required. | +| Debug Bundle ID | com.example.debug | Required if Debug Bundle ID is different than Release Bundle ID. | +| Release App Scheme | example:// | Required for use cases where iOS doesn't support universal links. | +| Debug App Scheme | exampleDebug:// | Required if Debug App Scheme is different than Release App Scheme. | +| App ID Prefix | ABCDE12345 | Required. | diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/data-points.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/data-points.mdx deleted file mode 100644 index 252bd3d63..000000000 --- a/src/content/docs/sdk/ios/v5/features/deep-links/data-points.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Retrieve data points -description: Retrieve the data required to set up deep links. -slug: en/sdk/ios/features/deep-links/data-points -sidebar-position: 1 -versions: - - label: v5 - value: v5 - default: true - - label: v4 - value: v4 -redirects: - v4: /en/sdk/ios/v4/features/deep-links/data-points ---- - -You need to retrieve the following data points before you can set up deep links in your app: - -- App ID Prefix -- Release Bundle ID -- Debug Bundle ID -- Release Custom URL Scheme -- Debug Custom URL Scheme -- Link resolution domain or domains - -## Instructions {#instructions} - -Follow these instructions to retrieve your data points. - -### App ID Prefix and Release Bundle ID {#app-id-prefix-and-release-bundle-id} - -Your App ID is found on the Apple Developer portal. It contains two parts: - -1. The **App ID prefix** -2. The **Bundle ID** - -The ID is formatted as `.`. For example: `ABC1234567.com.example.app` - -To find your App ID Prefix and Bundle ID, follow these steps: - -1. Log in to the [Apple Developer portal](https://developer.apple.com/account/). -2. Select **Certificates, IDs & Profiles** from the left-hand menu. -3. Select **Identifiers** from the left-hand menu. -4. Find your app and select it to open the edit page. -5. Your App ID Prefix and Bundle ID are displayed at the top of the page. Copy the relevant information and store it somewhere for later use. - -### Debug Bundle ID {#debug-bundle-id} - -If you're using a different bundle ID for your debug build, you can find its ID in Xcode. - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Signing & Capabilities** from the top menu. -5. Select **Debug** from the sub menu that appears. -6. Your Bundle ID is shown. Copy this information and store it somewhere for later use. - -### Custom URL schemes {#custom-url-schemes} - - - -A custom URL scheme is required for linking from other applications on the device, such as Telegram, X (formerly Twitter), and YouTube, or from push notifications. Check with your marketing team to see if a custom URL scheme is needed for the app. It's highly recommend to use the same custom URL scheme for iOS and Android. - - - -To retrieve your Custom URL Scheme, follow these steps: - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Info** from the top menu. -5. Expand the **URL Types** section and get the custom URL scheme. If the URL Schemes field contains a build setting (for example: `$(CUSTOM_URL_SCHEME)`), go to the build settings to retrieve the custom URL scheme values: - 1. Select **Build Settings** from the menu at the top. - 2. Find the setting named in the URL Schemes field and retrieve both the release and debug values. - -If your iOS app doesn't have a custom URL scheme yet, follow these steps to set a custom URL scheme: - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Info** from the top menu. -5. Expand the **URL Types** section. -6. Select the Add option to add a new URL type. -7. Fill in the following information to create a URL scheme: - - **Identifier**: `$(PRODUCT_BUNDLE_IDENTIFIER)` - - **URL Schemes**: your custom URL scheme. This must be unique. Don't use protected schemes such as `http`, `https`, or `mailto` - - **Role**: Editor - -This scheme will work for your production **and** debug builds. - -### Link Resolution domains {#link-resolution-domains} - - - -A link resolution domain is required for deep linking via email, SMS, QR codes, and platforms that shorten links. Check with your marketing team to see if [link resolution](https://help.adjust.com/en/article/link-resolution) is needed for the app. - - - -Your marketing team may already be using a link resolution domain for their email marketing platform. Get this domain from them and store it somewhere for later use. diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/deep-link.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/deep-link.mdx deleted file mode 100644 index 6ac3dc79d..000000000 --- a/src/content/docs/sdk/ios/v5/features/deep-links/deep-link.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Enable deep links in Adjust -description: Configure your app in Adjust to enable deep linking. -slug: en/sdk/ios/features/deep-links/deep-link -sidebar-position: 2 -versions: - - label: v5 - value: v5 - default: true - - label: v4 - value: v4 -redirects: - v4: /en/sdk/ios/v4/features/deep-links/deep-link ---- - -You need to configure your app in Adjust to enable deep linking. To do this, make sure you have done the following: - -- [ ] [Added your app in Adjust](https://help.adjust.com/en/article/app-setup). -- [ ] Retrieved all the required deep linking data. - -## Set up universal links in your app {#set-up-universal-links-in-your-app} - -To enable deep linking support for iOS 9 and later you need to set up universal links in Adjust. - - - -You can enter only one Bundle ID per app. If you are testing an app with a Debug Bundle ID, you need to create a separate app. - - - -Once you have gathered your setup data, you can add this to your app in Adjust. Adding the information to your app enables you to add deep links to your campaigns. To set up universal links, follow these steps: - -1. Go to **AppView** and select your app. -2. Ensure the **iOS bundle ID** is present. -3. Under **Device type**, choose your app's default device: - - Universal - iPhone and iPad - - iPhone - - iPad -4. Under **Universal linking**, turn on Enable universal linking to enable universal links for your app. - - Enter the **App ID prefix**. - - Enter the **App scheme**. -5. (Optional) Turn on **Redirect all clicks to a custom URL** and enter a **Custom URL**, if you want your users to go to a custom website instead of the App Store. This is recommended if your app doesn't have an App ID. -6. (Optional) Turn on **Send data to App Store Connect** and enter the **Apple provider ID** to send data to App Store Connect App Analytics. -7. Select **Save**. - -## Set up deep links with a custom URL scheme {#set-up-deep-links-with-a-custom-url-scheme} - -In this case, you need to pick a custom URL scheme name which your app will be responsible for opening. You can then use this scheme name in the Adjust link as part of the deep link parameter. - -To create a deep link with a custom URL scheme, follow these steps: - -1. Define the format of your custom URL scheme. If you are using a cross-platform framework, refer to the documentation for that framework to define the format of your custom URL scheme. Example: `example://summer-clothes?promo=beach` -2. URL encode the deep link. Example: `example%3A%2F%2Fsummer-clothes%3Fpromo%3Dbeach` -3. Pass this encoded deep link into an Adjust link. Example: `https://app.adjust.com/abc123?deeplink=%3A%2F%2Fsummer-clothes%3Fpromo%3Dbeach` -4. Append the `deeplink_js=1` parameter to the link with the encoded deep link. This forces the Adjust system to use the iOS custom URL scheme. Example: `https://app.adjust.com/abc123?deeplink_js=1&deeplink=%3A%2F%2Fsummer-clothes%3Fpromo%3Dbeach` diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/deferred.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/deferred.mdx deleted file mode 100644 index 91a661369..000000000 --- a/src/content/docs/sdk/ios/v5/features/deep-links/deferred.mdx +++ /dev/null @@ -1,199 +0,0 @@ ---- -title: Set up deferred deep linking -description: Configure deferred deep linking for your app. -slug: en/sdk/ios/features/deep-links/deferred -sidebar-position: 4 -versions: - - label: v5 - value: v5 - default: true - - label: v4 - value: v4 -redirects: - v4: /en/sdk/ios/v4/features/deep-links/deferred ---- - -A deferred deep link sends a user to a place in your app after routing them via the App Store to install the app first. - -## How it works {#how-it-works} - -This is how a deferred deep link works: - -1. The user clicks on an Adjust deep link. -2. Adjust's servers redirect the user to the App Store. -3. The user installs your app and opens it. -4. Adjust's servers perform attribution and send the deep link to the Adjust SDK. -5. Your app displays its preliminary content, such as onboarding screens and user login, if applicable. -6. Your app retrieves the deep link from the Adjust SDK and handles the deep link. - -## Setup {#setup} - - - -When you set up Adjust deferred deep linking, you need to disable deferred deep linking in other SDKs in your app. You also need to disable any deep linking setup from other MMPs. - - - - - -Check out the [Facebook deferred deep linking documentation](https://developers.facebook.com/docs/ios/deep-linking#deferred-deep-linking) for information about how to configure deep linking with your Facebook campaigns. - - - -There are 2 approaches to set up deferred deep linking in your app: - -1. Adjust's servers automatically pass the deferred deep link to the Adjust SDK. When the user opens your app, the Adjust SDK automatically calls the `open(_:options:completionHandler:)` method with the deep link. If your app doesn't have preliminary content (for example: onboarding screens and user login), or if your app already handles this content before opening the deep link, then no further configuration is required, and you can skip the rest of this section. -2. If your app has preliminary content (for example: onboarding screens and user login), but your app doesn't already handle this before handling the deep link, then you can add a deferred deep link listener. - -### Set up a deferred deep link listener {#set-up-a-deferred-deep-link-listener} - -1. Set up a delegate callback for deferred deep linking. If you have already configured attribution callbacks, you can skip this step. - - - - - - -```swift -class AppDelegate: UIResponder, UIApplicationDelegate, AdjustDelegate { -} -``` - - - - - - - - -```objc -@interface AppDelegate : UIResponder -``` - - - - - - -2. If you haven’t already done so, create an instance of the `ADJConfig` class and set a delegate on the `ADJConfig` object in your app delegate. You need to set the delegate in `ADJConfig` before initializing the SDK. - - - - -```swift -let yourAppToken = "{YourAppToken}" -let environment = ADJEnvironmentSandbox -let adjustConfig = ADJConfig( - appToken: yourAppToken, - environment: environment) -adjustConfig?.delegate = self - -// ... - -Adjust.initSdk(adjustConfig) -``` - - - - -```objc -ADJConfig *adjustConfig = [[ADJConfig alloc] initWithAppToken:@"{YourAppToken}" - environment:ADJEnvironmentSandbox]; -[adjustConfig setDelegate:self]; - -// ... - -[Adjust initSdk:adjustConfig]; -``` - - - - -3. Add the `adjustDeferredDeeplinkReceived` deferred deep link callback method to the delegate. The Adjust SDK calls this method after receiving a deferred deep link. - 1. Set your deep link handling code. - 2. Set the return value of the `adjustDeferredDeeplinkReceived` method to true or false. This indicates whether you want the Adjust SDK to call the `open(_:options:completionHandler:)` method to open the deep link after your deep link handling code runs. - - - - -```swift -func adjustDeferredDeeplinkReceived(_ deeplink: URL?) -> Bool { - // add your code below to handle deep link - // (for example, show onboarding screens, then open deep link content) - // deeplink object contains the deep link - - return false -} -``` - - - - -```objc -- (BOOL)adjustDeferredDeeplinkReceived:(NSURL *)deeplink { - // add your code below to handle deep link - // (for example, show onboarding screens, then open deep link content) - // deeplink object contains the deep link - - return NO; -} -``` - - - - -## Set up Adjust LinkMe {#set-up-adjust-linkme} - - - -```objc -- (void)enableLinkMe; -``` - - - - - -Discuss with your marketing team whether you need to implement LinkMe in your app. - - - -[Adjust’s LinkMe solution](https://help.adjust.com/en/article/linkme) is an optional feature that ensures robust deferred deep linking performance by enabling your app to read deep link information from the device pasteboard. - - - -The Adjust SDK checks the pasteboard when a user opens the app for the first time. The device displays a dialog asking if the user wants to allow the app to read the pasteboard. - - - -When a user clicks on a LinkMe URL they have the option to copy the link information to their system pasteboard. You can use the Adjust SDK to read the system pasteboard for deep link information. If deep link information is present, the Adjust SDK forwards the user to the correct page in your app. - -To enable pasteboard checking in your app, call the `[ADJConfig enableLinkMe]` method. - - - - -```swift -let yourAppToken = "{YourAppToken}" -let environment = ADJEnvironmentSandbox -let adjustConfig = ADJConfig( - appToken: yourAppToken, - environment: environment) -// ... -adjustConfig.enableLinkMe() -``` - - - - -```objc -NSString *yourAppToken = @"{YourAppToken}"; -NSString *environment = ADJEnvironmentSandbox; -ADJConfig *adjustConfig = [[ADJConfig alloc] initWithAppToken:@"{YourAppToken}" - environment:ADJEnvironmentSandbox]; -/// ... -[adjustConfig enableLinkMe]; -``` - - - diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/direct.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/direct.mdx deleted file mode 100644 index 8fcde1dcf..000000000 --- a/src/content/docs/sdk/ios/v5/features/deep-links/direct.mdx +++ /dev/null @@ -1,398 +0,0 @@ ---- -title: Set up direct deep linking -description: Configure direct deep linking for your app. -slug: en/sdk/ios/features/deep-links/direct -sidebar-position: 3 -versions: - - label: v5 - value: v5 - default: true - - label: v4 - value: v4 -redirects: - v4: /en/sdk/ios/v4/features/deep-links/direct ---- - -You can configure deep linking in your app after you have set it up in the Adjust dashboard. **Direct deep linking** occurs when a user has your app installed on their device. The link takes the user to a specific page within your app. - -To enable deep linking, you need to do the following: - -- [ ] Enable Associated Domains for your app -- [ ] Configure your deep links in Xcode - -## Enable Associated Domains {#enable-associated-domains} - -To get started, you need to enable Associated Domains in your Apple Developer account. This allows you to set universal link domains in your app. To do this, follow these steps: - -1. Log in to your [Apple Developer account](https://developer.apple.com/account/). -2. Select **Certificates, IDs & Profiles** in the left-hand menu. -3. Select **Identifiers** in the left-hand menu. -4. Find your app and select it to open the edit page. -5. Ensure that **Associated Domains** is checked under **Capabilities**. -6. Select **Save** to save your changes. - -## Configure deep links in Xcode {#configure-deep-links-in-xcode} - -Follow these steps to add your deep link configuration to your Xcode project. - -### Adjust universal links domain {#adjust-universal-links-domain} - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Signing & Capabilities** from the top menu. -5. Ensure that **All** is selected in the submenu below. -6. Select the Add option (**+**) to add a capability. -7. Select **Associated Domains**. -8. Enter the Adjust Universal Link domain with the prefix `applinks:` - - Here is an example using the `example.adj.st` domain: `applinks:example.adj.st`. - -### Custom URL scheme {#custom-url-scheme} - - - -Check with your marketing team to see if a custom URL scheme is needed for the app and discuss what the scheme name should be. If your app targets Android devices as well, use the same scheme name for each platform. - - - -1. Open your app project in Xcode. -2. Select your project from the left-hand menu. -3. Select your app under **Targets**. -4. Select **Info** from the top menu. -5. Expand the **URL Types** section. -6. Select the Add option (**+**) to add a URL type. -7. Fill in the following information to create a URL scheme: - - **Identifier**: `$(PRODUCT_BUNDLE_IDENTIFIER)` - - **URL Schemes**: Your custom URL scheme. This must be - unique. Don't use protected schemes such as `http`, `https`, or `mailto`. - - **Role**: Editor - -This scheme will work for your production **and** debug builds. - -## Modify your iOS app {#modify-your-ios-app} - -You need to update your iOS app to set up different deep linking scenarios. How you update your app depends on whether your app uses [scenes](https://developer.apple.com/documentation/uikit/app_and_environment/scenes). - -### App doesn't use scenes {#app-doesn-t-use-scenes} - -If your app doesn't uses scenes, you need to update methods in your app delegate. - -#### Universal links {#universal-links} - -Update the `application(_:continue:restorationHandler:)` method in your app delegate to call the following methods in the Adjust SDK: - -- `ADJLinkResolution.resolveLink`: Call this method only if your marketing team requires the use of Adjust's Link Resolution solution. If the deep link uses a domain that matches an element in the `resolveUrlSuffixArray`, then the method attempts to resolve the deep link, and returns the resolved link. If the deep link doesn't match an element in this array, then the method passes through the original deep link, so you can pass all deep links to this method. -- `Adjust.processDeeplink` - Call this method to send deep links to Adjust's servers to record information. You can pass both Adjust and non-Adjust deep links to this method. Adjust's servers will ignore any deep links that don’t have Adjust parameters. - -When a user clicks on your universal link, iOS opens your app and delivers the deep link to `application(_:continue:restorationHandler:)`. This occurs whether the user has closed your app or has it running in the background. - - - - -```swift -func application( - _ application: UIApplication, - continue userActivity: NSUserActivity, - restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) - -> Bool { - - if userActivity.activityType == NSUserActivityTypeBrowsingWeb { - let incomingURL = userActivity.webpageURL - - // call the below method to resolve deep link - ADJLinkResolution.resolveLink( - withUrl: incomingURL, - resolveUrlSuffixArray: ["email.example.com", "short.example.com"], - callback: { resolvedURL in - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - let deeplink = ADJDeeplink(deeplink: resolvedURL)! - Adjust.processDeeplink(deeplink) - }) - } else { - return false - } - - return true -} -``` - - - - -```objc -- (BOOL)application:(UIApplication *)application - continueUserActivity:(NSUserActivity *)userActivity - restorationHandler: - (void (^)(NSArray *restorableObjects))restorationHandler { - - if ([[userActivity activityType] - isEqualToString:NSUserActivityTypeBrowsingWeb]) { - NSURL *incomingURL = [userActivity webpageURL]; - - // call the below method to resolve deep link - [ADJLinkResolution - resolveLinkWithUrl:incomingURL - resolveUrlSuffixArray:@[ - @"email.example.com", @"short.example.com" - ] - callback:^(NSURL* _Nullable resolvedURL) { - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - ADJDeeplink *deeplink = [[ADJDeeplink alloc] initWithDeeplink:resolvedURL]; - [Adjust processDeeplink:deeplink]; - }]; - } else { - return NO; - } - - return YES; -} -``` - - - - -#### Custom URL scheme {#custom-url-scheme-1} - -If your marketing team requires you to set up custom URL scheme deep links, update the `application(_:open:options:)` method in your app delegate to call the `Adjust.processDeeplink` method in the Adjust SDK. This method sends deep links to Adjust's servers to record them. You can pass both Adjust and non-Adjust deep links to this method. Adjust's servers ignore any deep links that don’t have Adjust parameters. - -When a user clicks on your custom URL scheme deep link, iOS opens your app and delivers the deep link to `application(_:open:options:)`. This occurs whether the user has closed your app or has it running in the background. - - - - -```swift -func application( - _ app: UIApplication, - open incomingURL: URL, - options: [UIApplication.OpenURLOptionsKey: Any] = [:] - ) -> Bool { - - // add your code below to handle deep link - // (for example, open deep link content) - // incomingURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - Adjust.processDeeplink(ADJDeeplink(deeplink: incomingURL)) - - return true -} -``` - - - - -```objc -- (BOOL)application:(UIApplication *)app - openURL:(NSURL *)incomingURL - options:(NSDictionary *)options { - - // add your code below to handle deep link - // (for example, open deep link content) - // incomingURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - [Adjust processDeeplink:[[ADJDeeplink alloc] initWithDeeplink:incomingURL]]; - - return YES; -} -``` - - - - -### App uses scenes {#app-uses-scenes} - -If your app uses scenes, you need to update methods in your scene delegate. - -#### Universal links {#universal-links-1} - -1. Update the `scene(_:willConnectTo:options:)` method in your scene delegate. When a user clicks on your universal links and the user has your app closed, iOS opens your app and delivers the deep link to this method. -2. Update the `scene(_:continue:)` method in your scene delegate. When a user clicks on your universal links, and the user has your app running in the background, iOS opens your app and delivers the deep link to this method. - -The above methods call the following methods in the Adjust SDK: - -- `ADJLinkResolution.resolveLink`: Call this method only if your marketing team requires the use of Adjust's Link Resolution solution. If the deep link uses a domain that matches an element in the `resolveUrlSuffixArray`, then the method attempts to resolve the deep link, and returns the resolved link. If the deep link doesn't match an element in this array, then the method passes through the original deep link, so you can pass all deep links to this method. -- `Adjust.processDeeplink` - Call this method to send deep links to Adjust's servers to record them. You can pass both Adjust and non-Adjust deep links to this method. Adjust's servers ignore any deep links that don’t have Adjust parameters. - - - - -```swift -func scene( - _ scene: UIScene, - willConnectTo session: UISceneSession, - options connectionOptions: UIScene.ConnectionOptions - ) { - - guard let userActivity = connectionOptions.userActivities.first, - userActivity.activityType == NSUserActivityTypeBrowsingWeb, - let incomingURL = userActivity.webpageURL - else { return } - - // call the below method to resolve deep link - ADJLinkResolution.resolveLink( - withUrl: incomingURL, - resolveUrlSuffixArray: ["email.example.com", "short.example.com"], - callback: { resolvedURL in - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - let deeplink = ADJDeeplink(deeplink: resolvedURL)! - Adjust.processDeeplink(deeplink) - }) -} - -func scene( - _ scene: UIScene, - continue userActivity: NSUserActivity) { - - if userActivity.activityType == NSUserActivityTypeBrowsingWeb { - let incomingURL = userActivity.webpageURL - - // call the below method to resolve deep link - ADJLinkResolution.resolveLink( - withUrl: incomingURL, - resolveUrlSuffixArray: ["email.example.com", "short.example.com"], - callback: { resolvedURL in - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - let deeplink = ADJDeeplink(deeplink: resolvedURL)! - Adjust.processDeeplink(deeplink) - }) - } -} -``` - - - - -```objc -- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session - options:(UISceneConnectionOptions *)connectionOptions { - - NSUserActivity* userActivity = - [[[connectionOptions userActivities] allObjects] firstObject]; - - if ([[userActivity activityType] - isEqualToString:NSUserActivityTypeBrowsingWeb]) { - NSURL *incomingURL = [userActivity webpageURL]; - - // call the below method to resolve deep link - [ADJLinkResolution - resolveLinkWithUrl:incomingURL - resolveUrlSuffixArray:@[ - @"email.example.com", @"short.example.com" - ] - callback:^(NSURL* _Nullable resolvedURL) { - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - ADJDeeplink *deeplink = [[ADJDeeplink alloc] initWithDeeplink:resolvedURL]; - [Adjust processDeeplink:deeplink]; - }]; - } -} - -- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity{ - if ([[userActivity activityType] - isEqualToString:NSUserActivityTypeBrowsingWeb]) { - NSURL *incomingURL = [userActivity webpageURL]; - - // call the below method to resolve deep link - [ADJLinkResolution - resolveLinkWithUrl:incomingURL - resolveUrlSuffixArray:@[ - @"email.example.com", @"short.example.com" - ] - callback:^(NSURL* _Nullable resolvedURL) { - // add your code below to handle deep link - // (for example, open deep link content) - // resolvedURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - ADJDeeplink *deeplink = [[ADJDeeplink alloc] initWithDeeplink:resolvedURL]; - [Adjust processDeeplink:deeplink]; - }]; - } -} -``` - - - - -#### Custom URL scheme {#custom-url-scheme-2} - -1. Update the `scene(_:willConnectTo:options:)` method in your scene delegate. When a user clicks on your custom URL scheme deep link and the user has your app closed, iOS opens your app and delivers the deep link to this method. -2. Update the `scene(_:openURLContexts:)` method in your scene delegate. When a user clicks on your custom URL scheme deep link, and the user has your app running in the background, iOS opens your app and delivers the deep link to this method. - -These methods call the `Adjust.appWillOpen` method in the Adjust SDK. This method sends deep links to Adjust's servers to recordd them. You can pass both Adjust and non-Adjust deep links to this method. Adjust's servers ignore any deep links that don’t have Adjust parameters. - - - - -```swift -func scene( - _ scene: UIScene, - openURLContexts URLContexts: Set - ) { - - guard let incomingURL = URLContexts.first?.url else { - return - } - - // add your code below to handle deep link - // (for example, open deep link content) - // incomingURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - Adjust.processDeeplink(incomingURL) -} -``` - - - - -```objc -- (void)scene:(UIScene *)scene - openURLContexts:(nonnull NSSet *)URLContexts { - - NSURL *incomingURL = [[URLContexts allObjects] firstObject].URL; - - if (incomingURL) { - // add your code below to handle deep link - // (for example, open deep link content) - // incomingURL object contains the deep link - - - // call the below method to send deep link to Adjust's servers - [Adjust processDeeplink:incomingURL]; - } -} -``` - - - diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/index.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/index.mdx index e30dee6d6..43f6953ac 100644 --- a/src/content/docs/sdk/ios/v5/features/deep-links/index.mdx +++ b/src/content/docs/sdk/ios/v5/features/deep-links/index.mdx @@ -13,10 +13,3 @@ versions: redirects: v4: /en/sdk/ios/v4/features/deep-links --- - -You can create deep links to take users to specific pages in your app. The Adjust SDK uses different logic depending on if the user already has your app installed on their device: - -- Direct deep linking: occurs if the user already has your app installed. The link takes the user to the page specified in the link -- Deferred deep linking: occurs if the user doesn't have your app installed. The link takes the user to a storefront to install your app first. After the user installs the app, it opens to the page specified in the link. - -The SDK can read deep link data after a user opens your app from a link. Follow the steps in this section to get started. diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/resolution.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/resolution.mdx index 37ea23798..160b4b461 100644 --- a/src/content/docs/sdk/ios/v5/features/deep-links/resolution.mdx +++ b/src/content/docs/sdk/ios/v5/features/deep-links/resolution.mdx @@ -1,10 +1,8 @@ --- title: Link resolution -description: - Set up link resolution for deep linking via email, SMS, QR codes, and - platforms that shorten links. +description: Set up link resolution for deep linking via email, SMS, QR codes, and platforms that shorten links. slug: en/sdk/ios/features/deep-links/resolution -sidebar-position: 5 +sidebar-position: 3 versions: - label: v5 value: v5 diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/set-up-deep-linking.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/set-up-deep-linking.mdx new file mode 100644 index 000000000..c614a460d --- /dev/null +++ b/src/content/docs/sdk/ios/v5/features/deep-links/set-up-deep-linking.mdx @@ -0,0 +1,977 @@ +--- +title: Set up deep linking +description: Set up deep linking in your app. +slug: en/sdk/ios/features/deep-links/set-up-deep-linking +sidebar-position: 2 +versions: + - label: v5 + value: v5 + default: true + - label: v4 + value: v4 +redirects: + v4: /en/sdk/ios/v4/features/deep-links/set-up-deep-linking +--- + +Please follow the steps in the section that corresponds to the type of app you have: + +- [UIKit apps using AppDelegate lifecycle](#uikit-apps-using-appdelegate-lifecycle) +- [UIKit apps using SceneDelegate lifecycle](#uikit-apps-using-scenedelegate-lifecycle) +- [SwiftUI apps using AppDelegate lifecycle](#swiftui-apps-using-appdelegate-lifecycle) +- [SwiftUI apps using SceneDelegate lifecycle](#swiftui-apps-using-scenedelegate-lifecycle) + +In addition, implement deep link handling logic: + +- [Deep link handler](#deep-link-handler) + +## UIKit apps using AppDelegate lifecycle + +Update your AppDelegate to implement direct and deferred deep linking. + + + + + + +```swift +import Adjust +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate, + AdjustDelegate +{ + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: + [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + // Configure Adjust SDK + // Replace {YourAppToken} with your Adjust app token + let appToken = "{YourAppToken}" + var adjustConfig: ADJConfig? + + #if DEBUG + adjustConfig = ADJConfig( + appToken: appToken, + environment: ADJEnvironmentSandbox) + adjustConfig?.logLevel = ADJLogLevelVerbose + #else + adjustConfig = ADJConfig( + appToken: appToken, + environment: ADJEnvironmentProduction, + allowSuppressLogLevel: true) + adjustConfig?.logLevel = ADJLogLevelSuppress + #endif + + // Wait up to 120 seconds after app open for user to respond to ATT + // before sending install session to Adjust's servers. + // Ensure this interval is long enough for user to respond. + adjustConfig?.attConsentWaitingInterval = 120 + + // Create delegate for deferred deep linking + adjustConfig?.delegate = self + + // Initialize Adjust SDK + Adjust.initSdk(adjustConfig) + + return true + } + + // Receive universal link when app is installed, + // and app is in "not running" or background state, + // and user clicks link or another app opens link using + // UIApplication.open(_:options:completionHandler:). + func application( + _ application: UIApplication, + continue userActivity: NSUserActivity, + restorationHandler: + @escaping ([UIUserActivityRestoring]?) -> Void + ) -> Bool { + if userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingLink = userActivity.webpageURL + { + // Handle incoming universal link + DeeplinkHandler.handleDeeplink(incomingLink) + } + return true + } + + // Receive app scheme deep link when app is installed, and either: + // - App is in "not running" or background state, and user clicks link or + // another app opens link using UIApplication.open(_:options:completionHandler:), or + // - App is in foreground state and opens its own link using + // UIApplication.open(_:options:completionHandler:). + func application( + _ application: UIApplication, + open incomingLink: URL, + options: [UIApplication.OpenURLOptionsKey: Any] = [:] + ) -> Bool { + // Handle incoming app scheme deep link + DeeplinkHandler.handleDeeplink(incomingLink) + return true + } + + // Receive deferred deep link via AdjustDelegate method + func adjustDeferredDeeplinkReceived(_ deeplink: URL?) -> Bool { + if let incomingLink = deeplink { + if UserDefaults.standard.bool(forKey: "HasCompletedOnboarding") { + // If onboarding is complete, handle deferred deep link immediately + DeeplinkHandler.handleDeeplink(incomingLink) + } else { + // Store deferred deep link to invoke after onboarding screens and login + UserDefaults.standard.set(incomingLink.absoluteString, forKey: "lastDeferredLink") + } + } + // Return true to let Adjust SDK attempt to open deep link immediately + // upon receipt (for example: app has no ATT, onboarding screens, or login). + // Otherwise, return false to prevent SDK from opening the deep link immediately. + return false + } +``` + + + + + + + + +```objc +#import +#import "AppDelegate.h" +#import "DeeplinkHandler.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Configure Adjust SDK + // Replace {YourAppToken} with your Adjust app token + NSString *appToken = @"{YourAppToken}"; + ADJConfig *adjustConfig; + +#ifdef DEBUG + adjustConfig = [ADJConfig configWithAppToken:appToken + environment:ADJEnvironmentSandbox]; + [adjustConfig setLogLevel:ADJLogLevelVerbose]; +#else + adjustConfig = [ADJConfig configWithAppToken:appToken + environment:ADJEnvironmentProduction + allowSuppressLogLevel:YES]; + [adjustConfig setLogLevel:ADJLogLevelSuppress]; +#endif + + // Wait up to 120 seconds after app open for user to respond to ATT + // before sending install session to Adjust's servers. + // Ensure this interval is long enough for user to respond. + adjustConfig.attConsentWaitingInterval = 120; + + // Create delegate for deferred deep linking + adjustConfig.delegate = self; + + // Initialize Adjust SDK + [Adjust initSdk:adjustConfig]; + + return YES; +} + +// Receive universal link when app is installed, +// and app is in "not running" or background state, +// and user clicks link or another app opens link using +// [UIApplication openURL:options:completionHandler:]. +- (BOOL)application:(UIApplication *)application + continueUserActivity:(NSUserActivity *)userActivity + restorationHandler: + (void (^)(NSArray> *_Nullable)) + restorationHandler { + if ([userActivity.activityType + isEqualToString:NSUserActivityTypeBrowsingWeb]) { + NSURL *incomingLink = userActivity.webpageURL; + if (incomingLink) { + // Handle incoming universal link + [DeeplinkHandler handleDeeplink:incomingLink]; + } + } + return YES; +} + +// Receive app scheme deep link when app is installed, and either: +// - App is in "not running" or background state, and user clicks link or +// another app opens link using [UIApplication +// openURL:options:completionHandler:], or +// - App is in foreground state and opens its own link using +// [UIApplication openURL:options:completionHandler:]. +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)incomingLink + options: + (NSDictionary *)options { + // Handle incoming app scheme deep link + [DeeplinkHandler handleDeeplink:incomingLink]; + return YES; +} + +// Receive deferred deep link via AdjustDelegate method +- (BOOL)adjustDeferredDeeplinkReceived:(nullable NSURL *)deeplink { + if (deeplink) { + if ([[NSUserDefaults standardUserDefaults] + boolForKey:@"HasCompletedOnboarding"]) { + // If onboarding is complete, handle deferred deep link immediately + [DeeplinkHandler handleDeeplink:deeplink]; + } else { + // Store deferred deep link to invoke after onboarding screens and login + [[NSUserDefaults standardUserDefaults] + setObject:deeplink.absoluteString + forKey:@"lastDeferredLink"]; + } + } + // Return YES to let Adjust SDK attempt to open deep link immediately + // upon receipt (for example: app has no ATT, onboarding screens, or login). + // Otherwise, return NO to prevent SDK from opening the deep link immediately. + return NO; +} +``` + + + + + + +Most apps have some kind of onboarding process (for example: ATT prompt, onboarding screens, login prompt). When setting up deferred deep linking, you have to ensure that onboarding and launching deferred deep links don't interfere with each other. One approach is to wait until after onboarding to launch deferred deep links. Here's how it works in the code examples: + +1. A user who doesn't have the app installed clicks an Adjust deep link, which redirects them to the app store. +2. The user installs and opens the app. +3. The app begins its onboarding process. +4. After the ATT prompt response or timeout, the Adjust SDK sends session and attribution requests to Adjust's servers. +5. Adjust's servers respond with attribution data, including the deep link the user clicked on ("deferred deep link"). +6. The Adjust SDK triggers a deferred deep link callback in the app (shown in the AppDelegate implementation above). The callback checks whether onboarding is complete: + - If onboarding is complete, it handles the deep link immediately. + - If onboarding isn't complete, it stores the deep link. +7. Once onboarding completes, the app checks for and handles any stored deferred deep link (shown in the ViewController example class below). +8. The app navigates the user to the deep link screen. + + + + + + +```swift +import Adjust +import UIKit + +class ViewController: UIViewController { + var hasCompletedOnboarding: Bool { + get { + UserDefaults.standard.bool(forKey: "HasCompletedOnboarding") + } + set { + UserDefaults.standard.set(newValue, forKey: "HasCompletedOnboarding") + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + // Check if onboarding has been completed + if !hasCompletedOnboarding { + // Show onboarding screens and login prompt + + // Show ATT prompt after delay to ensure app is active + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + Adjust.requestTrackingAuthorizationWithCompletionHandler { _ in } + } + + // After onboarding, set hasCompletedOnboarding to true + hasCompletedOnboarding = true + + // Check for deferred deep link that arrived during onboarding + if let deferredLinkString = UserDefaults.standard.string( + forKey: "lastDeferredLink" + ), + let deferredLink = URL(string: deferredLinkString) { + // Remove stored deferred deep link to avoid handling again later + UserDefaults.standard.removeObject(forKey: "lastDeferredLink") + // Handle deferred deep link + DeeplinkHandler.handleDeeplink(deferredLink) + } + } else { + // Show main content + } + } +} +``` + + + + + + + + +```objc +#import +#import "DeeplinkHandler.h" +#import "ViewController.h" + +@implementation ViewController + +- (BOOL)hasCompletedOnboarding { + return [[NSUserDefaults standardUserDefaults] + boolForKey:@"HasCompletedOnboarding"]; +} + +- (void)setHasCompletedOnboarding:(BOOL)hasCompletedOnboarding { + [[NSUserDefaults standardUserDefaults] setBool:hasCompletedOnboarding + forKey:@"HasCompletedOnboarding"]; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + // Check if onboarding has been completed + if (!self.hasCompletedOnboarding) { + // Show onboarding screens and login prompt + // Show ATT prompt after delay to ensure app is active + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + [Adjust requestTrackingAuthorizationWithCompletionHandler:nil]; + }); + + // After onboarding, set hasCompletedOnboarding to true + self.hasCompletedOnboarding = YES; + + // Check for deferred deep link that arrived during onboarding + NSString *deferredLinkString = + [[NSUserDefaults standardUserDefaults] stringForKey:@"lastDeferredLink"]; + NSURL *deferredLink = [NSURL URLWithString:deferredLinkString]; + if (deferredLink) { + // Remove stored deferred deep link to avoid handling again later + [[NSUserDefaults standardUserDefaults] + removeObjectForKey:@"lastDeferredLink"]; + // Handle deferred deep link + [DeeplinkHandler handleDeeplink:deferredLink]; + } + } else { + // Show main content + } +} + +@end +``` + + + + + + +Lastly, implement your deep link handling logic: + +[Deep link handler](#deep-link-handler) + +## UIKit apps using SceneDelegate lifecycle + +Follow the steps in the [UIKit apps using AppDelegate lifecycle section](#uikit-apps-using-appdelegate-lifecycle), except instead of implementing the `application(_:continue:restorationHandler:)` and `application(_:open:options:)` methods in your AppDelegate for direct deep linking, implement the following methods in your SceneDelegate. + + + + + + +```swift +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + // Receive universal link or app scheme deep link + // when app is installed, and app is in "not running" state, and either: + // - User clicks link, or + // - Another app opens link using + // UIApplication.open(_:options:completionHandler:). + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + // Handle incoming universal link + if let userActivity = connectionOptions.userActivities.first, + userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingLink = userActivity.webpageURL + { + DeeplinkHandler.handleDeeplink(incomingLink) + } + + // Handle incoming app scheme deep link + else if let urlContext = connectionOptions.urlContexts.first { + let incomingLink = urlContext.url + DeeplinkHandler.handleDeeplink(incomingLink) + } + } + + // Receive universal link when app is installed, + // and app is in background state, and either: + // - User clicks link, or + // - Another app opens link using + // UIApplication.open(_:options:completionHandler:). + func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + if userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingLink = userActivity.webpageURL + { + // Handle incoming universal link + DeeplinkHandler.handleDeeplink(incomingLink) + } + } + + // Receive app scheme deep link when app is installed, and either: + // - App is in background state, and user clicks link or another app opens link using + // UIApplication.open(_:options:completionHandler:), or + // - App is in foreground and opens its own link using + // UIApplication.open(_:options:completionHandler:). + func scene( + _ scene: UIScene, openURLContexts URLContexts: Set + ) { + if let urlContext = URLContexts.first { + let incomingLink = urlContext.url + + // Handle incoming app scheme deep link + DeeplinkHandler.handleDeeplink(incomingLink) + } + } +} +``` + + + + + + + + +```objc +#import "DeeplinkHandler.h" +#import "SceneDelegate.h" + +@implementation SceneDelegate + +// Receive universal link or app scheme deep link +// when app is installed, and app is in "not running" state, and either: +// - User clicks link, or +// - Another app opens link using +// [UIApplication openURL:options:completionHandler:]. +- (void)scene:(UIScene *)scene + willConnectToSession:(UISceneSession *)session + options:(UISceneConnectionOptions *)connectionOptions { + // Handle incoming universal link + if (connectionOptions.userActivities.count > 0) { + NSUserActivity *userActivity = + connectionOptions.userActivities.allObjects.firstObject; + if ([userActivity.activityType + isEqualToString:NSUserActivityTypeBrowsingWeb] && + userActivity.webpageURL) { + NSURL *incomingLink = userActivity.webpageURL; + [DeeplinkHandler handleDeeplink:incomingLink]; + } + } + // Handle incoming app scheme deep link + else if (connectionOptions.URLContexts.count > 0) { + UIOpenURLContext *urlContext = + connectionOptions.URLContexts.allObjects.firstObject; + NSURL *incomingLink = urlContext.URL; + [DeeplinkHandler handleDeeplink:incomingLink]; + } +} + +// Receive universal link when app is installed, +// and app is in background state, and either: +// - User clicks link, or +// - Another app opens link using +// [UIApplication openURL:options:completionHandler:]. +- (void)scene:(UIScene *)scene + continueUserActivity:(NSUserActivity *)userActivity { + if ([userActivity.activityType + isEqualToString:NSUserActivityTypeBrowsingWeb]) { + NSURL *incomingLink = userActivity.webpageURL; + + // Handle incoming universal link + [DeeplinkHandler handleDeeplink:incomingLink]; + } +} + +// Receive app scheme deep link when app is installed, and either: +// - App is in background state, and user clicks link or another app opens link +// using [UIApplication openURL:options:completionHandler:], or +// - App is in foreground and opens its own link using +// [UIApplication openURL:options:completionHandler:]. +- (void)scene:(UIScene *)scene + openURLContexts:(NSSet *)URLContexts { + UIOpenURLContext *urlContext = URLContexts.allObjects.firstObject; + if (urlContext) { + NSURL *incomingLink = urlContext.URL; + + // Handle incoming app scheme deep link + [DeeplinkHandler handleDeeplink:incomingLink]; + } +} + +@end +``` + + + + + + +## SwiftUI apps using AppDelegate lifecycle + +If you haven't already done so, create an `AppDelegate.swift` file in your project's main directory and reference it in your main application file (for example: `App.swift` as shown below). This is required to handle app lifecycle events and Adjust SDK integration. Also implement `onOpenURL`, which receives all direct deep links. + + + + + + +```swift +import SwiftUI + +@main +struct MyApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + var body: some Scene { + WindowGroup { + ContentView() + // Receive universal link or app scheme deep link + // when app is installed, and app is in "not running" or background state, and either: + // - User clicks link, or + // - Another app opens link using + // UIApplication.open(_:options:completionHandler:) or SwiftUI's openURL. + // + // Receive app scheme deep link when app is installed, + // and app is in foreground and opens its own link using + // UIApplication.open(_:options:completionHandler:) or SwiftUI's openURL. + .onOpenURL { incomingLink in + DeeplinkHandler.handleDeeplink(incomingLink) + } + } + } +} +``` + + + + + + +Update your AppDelegate to implement deferred deep linking. + + + + + + +```swift +import Adjust +import UIKit + +class AppDelegate: UIResponder, UIApplicationDelegate, AdjustDelegate { + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: + [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + // Configure Adjust SDK + // Replace {YourAppToken} with your Adjust app token + let appToken = "{YourAppToken}" + var adjustConfig: ADJConfig? + + #if DEBUG + adjustConfig = ADJConfig( + appToken: appToken, + environment: ADJEnvironmentSandbox) + adjustConfig?.logLevel = ADJLogLevelVerbose + #else + adjustConfig = ADJConfig( + appToken: appToken, + environment: ADJEnvironmentProduction, + allowSuppressLogLevel: true) + adjustConfig?.logLevel = ADJLogLevelSuppress + #endif + + // Wait up to 120 seconds after app open for user to respond to ATT + // before sending install session to Adjust's servers. + // Ensure this interval is long enough for user to respond. + adjustConfig?.attConsentWaitingInterval = 120 + + // Create delegate for deferred deep linking + adjustConfig?.delegate = self + + // Initialize Adjust SDK + Adjust.initSdk(adjustConfig) + + return true + } + + // Receive deferred deep link via AdjustDelegate method + func adjustDeferredDeeplinkReceived(_ deeplink: URL?) -> Bool { + if let incomingLink = deeplink { + if UserDefaults.standard.bool(forKey: "HasCompletedOnboarding") { + // If onboarding is complete, handle deferred deep link immediately + DeeplinkHandler.handleDeeplink(incomingLink) + } else { + // Store deferred deep link to invoke after onboarding screens and login + UserDefaults.standard.set(incomingLink.absoluteString, forKey: "lastDeferredLink") + } + } + // Return true to let Adjust SDK attempt to open deep link immediately + // upon receipt (for example: app has no ATT, onboarding screens, or login). + // Otherwise, return false to prevent SDK from opening the deep link immediately. + return false + } +} +``` + + + + + + +Most apps have some kind of onboarding process (for example: ATT prompt, onboarding screens, login prompt). When setting up deferred deep linking, you have to ensure that onboarding and launching deferred deep links don't interfere with each other. One approach is to wait until after onboarding to launch deferred deep links. Here's how it works in the code examples: + +1. A user who doesn't have the app installed clicks an Adjust deep link, which redirects them to the app store. +2. The user installs and opens the app. +3. The app begins its onboarding process. +4. After the ATT prompt response or timeout, the Adjust SDK sends session and attribution requests to Adjust's servers. +5. Adjust's servers respond with attribution data, including the deep link the user clicked on ("deferred deep link"). +6. The Adjust SDK triggers a deferred deep link callback in the app (shown in the AppDelegate implementation above). The callback checks whether onboarding is complete: + - If onboarding is complete, it handles the deep link immediately. + - If onboarding isn't complete, it stores the deep link. +7. Once onboarding completes, the app checks for and handles any stored deferred deep link (shown in the ContentView example class below). +8. The app navigates the user to the deep link screen. + + + + + + +```swift +import Adjust +import Foundation +import SwiftUI + +struct ContentView: View { + var body: some View { + NavigationStack { + VStack { + // Check if onboarding has been completed + if !UserDefaults.standard.bool(forKey: "HasCompletedOnboarding") { + // Show onboarding screens and login prompt + // Show ATT prompt after delay to ensure app is active + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + Adjust.requestTrackingAuthorizationWithCompletionHandler { _ in } + } + + // After onboarding, set hasCompletedOnboarding to true + UserDefaults.standard.set(true, forKey: "HasCompletedOnboarding") + + // Check for deferred deep link that arrived during onboarding + if let deferredLinkString = UserDefaults.standard.string( + forKey: "lastDeferredLink" + ), + let deferredLink = URL(string: deferredLinkString) { + // Remove stored deferred deep link to avoid handling again later + UserDefaults.standard.removeObject(forKey: "lastDeferredLink") + // Handle deferred deep link + DeeplinkHandler.handleDeeplink(deferredLink) + } + } + } else { + // Show main content + } + } + } + } +} +``` + + + + + + +Lastly, implement your deep link handling logic: + +[Deep link handler](#deep-link-handler) + +## SwiftUI apps using SceneDelegate lifecycle + +Follow the steps in the [SwiftUI apps using AppDelegate lifecycle](#swiftui-apps-using-appdelegate-lifecycle). In addition, implement the following method in your SceneDelegate. + + + + + + +```swift +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + // Receive universal link or app scheme deep link + // when app is installed, and app is in "not running" state, and either: + // - User clicks link, or + // - Another app opens link using + // UIApplication.open(_:options:completionHandler:) or SwiftUI's openURL. + func scene( + _ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + // Handle incoming universal link + if let userActivity = connectionOptions.userActivities.first, + userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingLink = userActivity.webpageURL + { + DeeplinkHandler.handleDeeplink(incomingLink) + } + // Handle incoming app scheme deep link + else if let urlContext = connectionOptions.urlContexts.first { + let incomingLink = urlContext.url + DeeplinkHandler.handleDeeplink(incomingLink) + } + } +} +``` + + + + + + +## Deep link handler + +The preceding code examples use an example DeeplinkHandler class. This example class is shown below and handles all types of links: + +- Adjust branded links (full go.link links) +- Adjust short branded links (short go.link links) +- Adjust universal links (adj.st links) +- Non-Adjust universal links (example.com links) +- App scheme deep links (example:// links) + +The class performs the following tasks: + +1. The class uses Adjust's `processAndResolveDeeplink` method, which sends the deep link to Adjust's servers to accomplish two things: + +- Record the deep link click for attribution purposes. +- If the deep link is an [Adjust short branded link](https://www.help.adjust.com/en/article/short-branded-links), respond with the corresponding full URL. Otherwise, respond with the original link. + +2. After processing, the class parses the link and navigates to the appropriate screen. This part of the code is specific to each app. Your app has to implement its own logic for handling deep links and opening the corresponding content. Your deep link handling has to meet the following key requirements: + +- Your app should treat Adjust branded links the same as other universal links, such as your own. For example, the following links should navigate to the same screen in your app: + - Adjust branded link: `https://example.go.link/summer-clothes?promo=beach` + - Your universal link: `https://www.example.com/summer-clothes?promo=beach` +- In cases where iOS doesn't support universal links, Adjust automatically converts them to app scheme deep links. Additionally, Adjust's servers convert all deferred deep links to app scheme deep link format. Therefore it's crucial for the app to handle universal links and app scheme deep links equivalently. For example, the following links should navigate to the same screen in your app: + - Adjust branded link: `https://example.go.link/summer-clothes?promo=beach` + - App scheme deep link: `example://summer-clothes?promo=beach` + + + + + + +```swift +import Adjust +// import SwiftUI and/or UIKit + +class DeeplinkHandler { + static func handleDeeplink(_ incomingLink: URL) { + // Send incoming deep link to Adjust's servers for attribution + // and retrieve full URL if short branded link. + // If not, retrieve original link. + let deeplink = ADJDeeplink(deeplink: incomingLink) + Adjust.processAndResolveDeeplink(deeplink) { processedLinkString in + guard let processedLink = URL(string: processedLinkString) else { return } + + // Extract path, query items, and fragment from the processed link. + guard let components = URLComponents( + url: processedLink, + resolvingAgainstBaseURL: true + ) else { return } + + // For app scheme deep links, set path = host + path + var path: String + if let scheme = processedLink.scheme, scheme == "http" || + scheme == "https" { + path = components.path + } else { + path = (components.host ?? "") + components.path + if !path.isEmpty && !path.hasPrefix("/") { + path = "/" + path + } + } + + let queryItems = components.queryItems ?? [] + // Parse query parameters into a dictionary for easier access + let params = queryItems.reduce(into: [String: String]()) { result, item in + result[item.name] = item.value + } + let fragment = components.fragment + + // Implement the navigation or other app-specific logic based on + // the deep link components. + DispatchQueue.main.async { + // Example of navigating based on the path or other components. + // Replace with your actual navigation logic. + switch path { + case "/product": + if let productId = params["id"] { + handleProduct(productId: productId) + } + case "/category": + if let categoryName = params["name"] { + handleCategory(category: categoryName) + } + case "/search": + if let query = params["q"] { + handleSearch(query: query) + } + default: + print("Unhandled deep link path: \(path)") + } + } + } + } + + private static func handleProduct(productId: String) { + // Example UIKit implementation: + // let productVC = ProductViewController(productId: productId) + // navigationController.pushViewController(productVC, animated: true) + + // Example SwiftUI implementation: + // let productView = ProductView(productId: productId) + // navigationPath.append(productView) + // or: router.navigate(to: productView) + } + + private static func handleCategory(category: String) { + // Example UIKit implementation: + // let categoryVC = CategoryViewController(category: category) + // navigationController.pushViewController(categoryVC, animated: true) + + // Example SwiftUI implementation: + // let categoryView = CategoryView(category: category) + // navigationPath.append(categoryView) + // or: router.navigate(to: categoryView) + } + + private static func handleSearch(query: String) { + // Example UIKit implementation: + // let searchVC = SearchViewController(searchQuery: query) + // navigationController.pushViewController(searchVC, animated: true) + + // Example SwiftUI implementation: + // let searchView = SearchView(searchQuery: query) + // navigationPath.append(searchView) + // or: router.navigate(to: productView) + } +} +``` + + + + + + + + +```objc +#import +#import "DeeplinkHandler.h" + +@implementation DeeplinkHandler + ++ (void)handleDeeplink:(NSURL *)incomingLink { + // Send incoming deep link to Adjust's servers for attribution + // and retrieve full URL if short branded link. + // If not, retrieve original link. + ADJDeeplink *deeplink = [[ADJDeeplink alloc] initWithDeeplink:incomingURL]; + [Adjust processAndResolveDeeplink:deeplink + withCompletionHandler:^(NSString *_Nonnull processedLinkString) { + NSURL *processedLink = [NSURL URLWithString:processedLinkString]; + if (!processedLink) return; + + // Extract path, query items, and fragment from the processed link. + NSURLComponents *components = + [NSURLComponents componentsWithURL:processedLink + resolvingAgainstBaseURL:YES]; + + // For app scheme deep links, set path = host + path + NSString *path; + if ([processedLink.scheme isEqualToString:@"http"] || + [processedLink.scheme isEqualToString:@"https"]) { + path = components.path; + } else { + path = [NSString stringWithFormat:@"%@%@", + components.host ?: @"", + components.path]; + if (path.length > 0 && ![path hasPrefix:@"/"]) { + path = [@"/" stringByAppendingString:path]; + } + } + + NSArray *queryItems = components.queryItems ?: @[]; + + // Parse query parameters into a dictionary for easier access + NSMutableDictionary *params = + [NSMutableDictionary dictionary]; + for (NSURLQueryItem *item in queryItems) { + params[item.name] = item.value; + } + + NSString *fragment = components.fragment; + + // Implement the navigation or other app-specific logic based on + // the deep link components. + dispatch_async(dispatch_get_main_queue(), ^{ + // Example of navigating based on the path or other components. + // Replace with your actual navigation logic. + if ([path isEqualToString:@"/product"]) { + NSString *productId = params[@"id"]; + if (productId) { + ProductViewController *productVC = + [[ProductViewController alloc] initWithProductId:productId]; + [self navigateToViewController:productVC]; + } + } else if ([path isEqualToString:@"/category"]) { + NSString *categoryName = params[@"name"]; + if (categoryName) { + CategoryViewController *categoryVC = + [[CategoryViewController alloc] initWithCategory:categoryName]; + [self navigateToViewController:categoryVC]; + } + } else if ([path isEqualToString:@"/search"]) { + NSString *query = params[@"q"]; + if (query) { + SearchViewController *searchVC = + [[SearchViewController alloc] initWithSearchQuery:query]; + [self navigateToViewController:searchVC]; + } + } else { + NSLog(@"Unhandled deep link path: %@", path); + } + }); + }]; +} + ++ (void)navigateToViewController:(UIViewController *)viewController { + // Example navigation implementation: + // UINavigationController *navigationController = [self + // getNavigationController]; [navigationController + // pushViewController:viewController animated:YES]; +} + +@end +``` + + + + + diff --git a/src/content/docs/sdk/ios/v5/features/deep-links/testing.mdx b/src/content/docs/sdk/ios/v5/features/deep-links/testing.mdx index 2804f5a83..ade483c31 100644 --- a/src/content/docs/sdk/ios/v5/features/deep-links/testing.mdx +++ b/src/content/docs/sdk/ios/v5/features/deep-links/testing.mdx @@ -2,7 +2,7 @@ title: Test deep linking description: Test your deep links to ensure they work as expected. slug: en/sdk/ios/features/deep-links/testing -sidebar-position: 6 +sidebar-position: 4 versions: - label: v5 value: v5 diff --git a/src/utils/helpers/navigation/buildSidebarHierarchy.ts b/src/utils/helpers/navigation/buildSidebarHierarchy.ts index f07883315..197562897 100644 --- a/src/utils/helpers/navigation/buildSidebarHierarchy.ts +++ b/src/utils/helpers/navigation/buildSidebarHierarchy.ts @@ -66,6 +66,19 @@ export const buildSidebarHierarchy = (entries: ContentCollectionEntry[]): [Langu }); }; + // Sort function for sidebar items + const sortSidebarItems = (a: SidebarItem, b: SidebarItem) => { + // If both have positions, sort by position + if (a.position !== undefined && b.position !== undefined) { + return a.position - b.position; + } + // If only one has position, positioned items come first + if (a.position !== undefined) return -1; + if (b.position !== undefined) return 1; + // If neither has position, sort alphabetically by title + return a.title.localeCompare(b.title); + }; + // Build the parent-child relationships sortedEntries.forEach(entry => { // Fetch the entry from the slug map @@ -100,12 +113,8 @@ export const buildSidebarHierarchy = (entries: ContentCollectionEntry[]): [Langu // Set the parent for the child potentialChild.parent = entry.slug; - // Insert the child in the correct position or alphabetically - if (potentialChild.position) { - structuredEntry.children?.splice(potentialChild.position - 1, 0, potentialChild); - } else { - structuredEntry.children?.push(potentialChild); - } + // Add child to the children array + structuredEntry.children?.push(potentialChild); // Remove the entry from the root array since it's now in a child array if (type === "sdk") { @@ -116,22 +125,8 @@ export const buildSidebarHierarchy = (entries: ContentCollectionEntry[]): [Langu } }); - // Sort the children alphabetically if they have no position - structuredEntry.children?.sort((a, b) => { - if (a.position && b.position) { - return 0; - } - if (a.position) { - return -1; - } - if (b.position) { - return 1; - } - // Sort alphabetically by file name (last part of the id) - const aFileName = a.id.split('/').pop()?.toLowerCase(); - const bFileName = b.id.split('/').pop()?.toLowerCase(); - return aFileName?.localeCompare(bFileName!); - }); + // Sort children by position + structuredEntry.children?.sort(sortSidebarItems); // If the entry has no parent, nest it directly under the type array if (!structuredEntry.parent) { @@ -146,5 +141,9 @@ export const buildSidebarHierarchy = (entries: ContentCollectionEntry[]): [Langu setChildLevels(structuredEntry, structuredEntry.level); }); - return [hierarchy, slugMap]; // Return sdk and api arrays inside the hierarchy object -}; + // Sort top-level entries by position + hierarchy.sdk.sort(sortSidebarItems); + hierarchy.api.sort(sortSidebarItems); + + return [hierarchy, slugMap]; +}; \ No newline at end of file