Adjust Deeplink + react-native-inappbrowser-reborn 深度鏈接的踩坑之路

Adjust Deeplink

Adjust 它的核心功能是 跟蹤和分析、歸因分析、深度鏈接、廣告花費(fèi)追蹤、防欺詐工具、實(shí)時(shí)數(shù)據(jù)等等,它是非常強(qiáng)大也是市面上比較熱門(mén)營(yíng)銷(xiāo)工具,本次主要介紹深度鏈接。Adjust 的 SDK 包里已經(jīng)包含了深度鏈接的功能,所以不需要再額外下載依賴(lài),只需要在 Adjust 的后臺(tái)增加一些設(shè)定和微改原生代碼即可。

React-Native 配置深度鏈接

深度鏈接的配置,需要更改「IOS」和「Android」的原生代碼,以及使用 Adjust 提供的「深度鏈接生成器」生成出深度鏈接。下面會(huì)具體講述更改原生代碼和使用深度鏈接生成器,生出深度鏈接。也可以查看官網(wǎng) React-Native 配置。

IOS 通用鏈接設(shè)定

IOS 的通用鏈接:

  1. 打開(kāi) adjust 登入后臺(tái)地址
  2. 找到你需要設(shè)定的應(yīng)用,然后點(diǎn)擊應(yīng)用下的 灰色三角形(^) --> 「所有設(shè)置」
  3. 完成上面的點(diǎn)擊流程,右邊會(huì)出現(xiàn)側(cè)邊欄菜單,請(qǐng)點(diǎn)擊「平臺(tái)」--> 「應(yīng)用類(lèi)型請(qǐng)選擇 IOS」--> 「默認(rèn)設(shè)備選擇通用」--> 「點(diǎn)擊通用鏈接(Universal Linking)」
  4. 應(yīng)用前綴(APP PREFIX),需要前往 Apple Developer 賬戶找到
  5. 應(yīng)用方案(APP SCHEME), 這個(gè)可以自定義即可,但必須是唯一性的
  6. 點(diǎn)擊保存之后,就可以得到一個(gè)原始通用鏈接,類(lèi)似 xxxx.adj.st 這樣的

如果遇到不明白的地方,可以查看官網(wǎng)的 IOS 通用鏈接配置 截圖流程,會(huì)比較詳細(xì)

啟用 Associated Domain(比較重要,能否開(kāi)啟 app,就看它了)

  1. 打開(kāi) Xcode 打開(kāi)項(xiàng)目
  2. 點(diǎn)擊左上角的文件夾 icon --> 點(diǎn)擊項(xiàng)目名稱(chēng) --> 進(jìn)入到「Signing & Capabilities」--> 點(diǎn)擊下方的 「all」--> 找到「Associated Domain」--> 點(diǎn)擊 「+」然后輸入 applinks:你的通用鏈接,例如 applinks:xxxx.adj.st。(前綴必須是 applinks:)
  3. 輸入好之后,按下鍵盤(pán)回車(chē)鍵(Enter)即可

增加 IOS 原始代碼

路徑是 ios/you_project/AppDelegate.m

增加代碼如下

#import <React/RCTLinkingManager.h>

- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}

<!-- 這段很重要,如果不加它的話,用戶通過(guò)點(diǎn)擊深度鏈接,把a(bǔ)pp從背景呼叫到前景時(shí),拿不到深度鏈接到參數(shù) -->
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
 restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
 return [RCTLinkingManager application:application
                  continueUserActivity:userActivity
                    restorationHandler:restorationHandler];
}

注意:保存代碼之后,請(qǐng) cd /ios 執(zhí)行 pod install,然后再重新 build 到真機(jī)上測(cè)試,請(qǐng)不要使用模擬器

具體詳細(xì)說(shuō)明可以查看React-native 的 Linking 文檔

Android 深度鏈接

需要更改的原生代碼如下

路徑:android/app/src/main/AndroidManifest.xml

<activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
    android:launchMode="singleTask" // 請(qǐng)選擇 singleTask 模式開(kāi)啟app
    android:screenOrientation="portrait"
    android:exported="true"
    android:windowSoftInputMode="adjustResize">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <!-- 主要是以下這段代碼,如果深度鏈接無(wú)法開(kāi)啟app,請(qǐng)檢查深度鏈接配置的 scheme 是否和下面代碼中的 scheme 一致-->
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="myapp"/>
    </intent-filter>
</activity>

注意:scheme 的內(nèi)容,可以根據(jù)自己的需求設(shè)定,但必須是唯一性的。請(qǐng)于上面配置 ios 的通用鏈接中的 「APP SCHEME」保持一致
具體詳細(xì)說(shuō)明可以查看 深度鏈接 的文檔

Adjust 深度鏈接生成器

  1. 打開(kāi)adjust 登入后臺(tái)地址
  2. 登入之后點(diǎn)擊左側(cè)菜單選擇「深度鏈接生成器」,如果你的后臺(tái)沒(méi)有出現(xiàn)的話,可能是權(quán)限不夠,需要增加權(quán)限
  3. PLATFORM 請(qǐng)選擇 【Multiplatform】(多平臺(tái))
  4. AD ENVIRONMENT 請(qǐng)選擇 【Other】
  5. FORMAT 請(qǐng)選擇 【ulink】(如果不選 ulink 的話,下面選項(xiàng)中就不會(huì)出現(xiàn) RAW UNIVERSAL LINK)
  6. APP 請(qǐng)選擇 你的項(xiàng)目
  7. TRACKER 請(qǐng)選擇 【Dynamic Link】(這些都可以自定義的,建議選擇相關(guān)的跟蹤途徑。另外提醒一下在設(shè)定鏈接的時(shí)候,請(qǐng)注意「用戶目的位置」這一項(xiàng)的子項(xiàng)中深度鏈接配置,"已安裝應(yīng)用的用戶跳轉(zhuǎn)到哪里?",請(qǐng)一定選擇「應(yīng)用內(nèi)界面」,否則用戶打開(kāi) app 之后,會(huì)跳轉(zhuǎn)到商店,這個(gè)很關(guān)鍵。如果遇到打開(kāi) app 之后會(huì)跳轉(zhuǎn)到商店時(shí),也請(qǐng)檢查這一項(xiàng)設(shè)定)
  8. RAW UNIVERSAL LINK 請(qǐng)?zhí)顚?xiě) 通用鏈接例如 xxx.adj.st(還可以設(shè)定自定義品牌鏈接,這個(gè)會(huì)在最后詳細(xì)說(shuō)明)
  9. ANDROID APP SCHEME 請(qǐng)?zhí)顚?xiě) 你項(xiàng)目中的 scheme,例如 myapp://
  10. IOS UNIVERSAL LINK PATH 和 ANDROID DEEPLINK PATH 請(qǐng)?zhí)顚?xiě)一樣的路徑參數(shù) 例如 navigate?pageName=Dame
  11. FALLBACK URL(optional) 這一項(xiàng)非必填,目前使用的地方是,在非移動(dòng)端的設(shè)備點(diǎn)擊深度鏈接時(shí),跳轉(zhuǎn)到指定的網(wǎng)頁(yè),例如:https://www.testdemo.com/
  12. REDIRECT MACOS URL(optional) 對(duì) ios 和 macos 有效,這一項(xiàng)非必填,目前使用的地方是,在非移動(dòng)端的設(shè)備點(diǎn)擊深度鏈接時(shí),跳轉(zhuǎn)到指定的網(wǎng)頁(yè),例如:https://www.testdemo.com/
  13. 點(diǎn)擊底部的 Generate Deeplink,等待完成之后,就會(huì)創(chuàng)建出一個(gè)深度鏈接,出現(xiàn)在右側(cè),這個(gè)時(shí)候可以復(fù)制它去嘗試 app 的設(shè)定是否有效了

完整的深度鏈接例如: https://testdemo.go.link/navigate?pageName=Dame&adj_t=19c9dyt6&adj_fallback=https%3A%2F%2Fwww.testdemo.com%2F&adj_redirect_macos=https%3A%2F%2Fwww.testdemo.com%2F

當(dāng)把 IOS 和 Android 的需要調(diào)整的地方都調(diào)整時(shí),在 build 到真機(jī)后,點(diǎn)擊深度鏈接不出意外的話,就可以順利開(kāi)啟 app 了,但想要獲取參數(shù),還需要進(jìn)一步更改 js 代碼

注意:在填寫(xiě) FALLBACK URL(optional) 和 REDIRECT MACOS URL(optional) 請(qǐng)一定要把跳轉(zhuǎn)網(wǎng)站后增加 “/”,不然 IOS 收不到任何參數(shù),范例格式:https://www.testdemo.com/

JS 代碼調(diào)整

以下代碼功能如下

  1. 接收深度鏈接的參數(shù)進(jìn)行格式化處理
  2. 把處理好的參數(shù)和事件存入 state 中
  3. 處理導(dǎo)頁(yè)的函數(shù)監(jiān)聽(tīng) state 改變,并符合導(dǎo)頁(yè)的條件后進(jìn)行導(dǎo)頁(yè)

代碼僅供參考,可以根據(jù)自己的業(yè)務(wù)需求修改

import { useState, useEffect } from "react";
import { Linking } from "react-native";
import { useSelector } from "react-redux";
import { NavigationContainerRefWithCurrent } from "@react-navigation/native";
import { RootState, DynamicLinkEventEnum, RedirectUrlType } from "@tcg/xoso_web_core";
import useRedirect from "~/hooks/useRedirect";
import useDynamicLinkStore, { pushTask, shiftTask } from "~/store/useDynamicLinkStore";

/** method: 將url params轉(zhuǎn)為key value物件 */
const urlQueryParser = (url: string): { [key: string]: string } => {
    const regex = /[?&]([^=#]+)=([^&#]*)/g;
    const params: any = {};
    let match;
    while ((match = regex.exec(url))) {
        params[match[1]] = match[2];
    }
    return params;
};

/** method: 動(dòng)態(tài)連結(jié)導(dǎo)頁(yè) */
const navigateByDynamicLink = ({ navigationRef }: { navigationRef: NavigationContainerRefWithCurrent<ReactNavigation.RootParamList> }) => {
    const systemInitialized = useSelector((state: RootState) => state.system.initialized);
    const hasProfile = useSelector((state: RootState) => !!state.auth.userProfile);
    const dynamicLinkTasks = useDynamicLinkStore((state) => state.dynamicLinkTasks);
    const [navigateItem, setNavigateItem] = useState<RedirectUrlType | undefined>(undefined);

    // 當(dāng)navigateItem改變時(shí)觸發(fā)導(dǎo)頁(yè)的hook
    useRedirect.redirectByUrlObj({
        navigationRef,
        urlObj: navigateItem,
        onFinish: () => {
            shiftTask();
        },
    });

    useEffect(() => {
        if (!systemInitialized || !dynamicLinkTasks || !navigationRef || !hasProfile) return; // 若尚未開(kāi)啟完app或無(wú)推播資料就不繼續(xù)
        // 當(dāng)推播資料有夾帶導(dǎo)頁(yè)資訊時(shí)進(jìn)行處理
        const oauthBindTask = Object.assign({}, dynamicLinkTasks[0]);
        if (oauthBindTask.event === DynamicLinkEventEnum.navigate && oauthBindTask.querys.pageName) {
            // 動(dòng)態(tài)連結(jié)會(huì)將導(dǎo)頁(yè)目標(biāo)及參數(shù)全放在querys,因此轉(zhuǎn)換過(guò)程需將
            const cloneQuery = Object.assign({}, oauthBindTask.querys);
            delete cloneQuery.pageName;
            const urlObj = {
                page: oauthBindTask.querys.pageName,
                params: cloneQuery,
            };
            setNavigateItem(urlObj);
        }
    }, [navigationRef, systemInitialized, dynamicLinkTasks, hasProfile]);
};

/** 啟動(dòng)監(jiān)聽(tīng)深度鏈接 */
const startListenerDeepLinking = () => {
    const handleLink = (link: any, isInit: boolean) => {
        try {
            if (!link.url) return;
            const url = link.url;
            // DynamicLinks挾帶的params參數(shù)物件
            const querys = urlQueryParser(url);
            // 定義應(yīng)處理的event範(fàn)圍
            const eventRange = [DynamicLinkEventEnum.navigate];
            if (isInit) {
                eventRange.push(DynamicLinkEventEnum.init_app);
            } else {
                eventRange.push(DynamicLinkEventEnum.foreground);
                eventRange.push(DynamicLinkEventEnum.oauth_bind);
            }
            // 屬於DynamicLinkEventEnum定義範(fàn)圍者才可放入task
            eventRange.forEach((event) => {
                if (url.includes(`${event}`)) {
                    pushTask({ event, querys });
                }
            });
        } catch (error) {
            console.log("handleLink error", error);
        }
    };

    // 獲取初始化深度鏈接
    const handleInitDynamicLinks = async () => {
        const url = await Linking.getInitialURL();
        handleLink({ url }, true);
    };

    useEffect(() => {
        handleInitDynamicLinks();
        // 添加深度鏈接監(jiān)聽(tīng)器
        const subscription = Linking.addEventListener("url", (event: any) => {
            handleLink(event, false);
        });

        // 清除監(jiān)聽(tīng)器,避免內(nèi)存泄漏
        return () => {
            subscription.remove();
        };
    }, []);
};

export default {
    startListenerDeepLinking,
    navigateByDynamicLink,
};

自定義品牌域名

Adjust 提供的域名都是不夠人性化的,很多時(shí)候想要使用自己的域名來(lái)做深度鏈接,Adjust 也考慮到了這一點(diǎn),所以它提供了自定義域名功能,操作如下;

  1. 打開(kāi)adjust 登入后臺(tái)地址
  2. 選擇你需要配置的應(yīng)用 app,并點(diǎn)擊 app 應(yīng)用模塊最下面的(^)
  3. 點(diǎn)擊所有設(shè)置 --> 自定義鏈接 例如域名為 testdemo
  4. 輸入唯一的子域以注冊(cè)點(diǎn)擊 URL 和深層鏈接中使用的子域
  5. 選擇保存
  6. 選擇 ?(勾號(hào)圖標(biāo))以確認(rèn)您已輸入正確的子域
  7. 把得到的自定義域名 例如:testdemo.go.link,去更改 Associated Domains
    1. 打開(kāi) Xcode 打開(kāi)項(xiàng)目
    2. 點(diǎn)擊左上角的文件夾 icon --> 點(diǎn)擊項(xiàng)目名稱(chēng) --> 進(jìn)入到「Signing & Capabilities」--> 點(diǎn)擊下方的 「all」--> 找到「Associated Domain」--> 雙擊之前配置的通用鏈接,然后替換成自定義域名,例如:testdemo.go.link
    3. 輸入好之后,按下鍵盤(pán)回車(chē)鍵(Enter)即可
  8. 回到 adjust 的 「深度鏈接生成器」,按照「Adjust 深度鏈接生成器」的設(shè)定,重新生成一遍,自定義域名會(huì)自動(dòng)生成到 RAW UNIVERSAL LINK。當(dāng)填寫(xiě)完之后,點(diǎn)擊最下面的 Generate Deeplink 就可以使用自定義域名的深度鏈接,進(jìn)行開(kāi)啟 app 了;

具體詳細(xì)說(shuō)明可以查看 深度鏈接的文檔

解決 InAppBrowser 打開(kāi)深度鏈接會(huì)直接開(kāi)啟商店

使用了 react-native-inappbrowser-reborn 在 app 內(nèi)部開(kāi)啟瀏覽器進(jìn)行第三方綁定認(rèn)證,等待完成之后的 redirect,開(kāi)啟深度鏈接時(shí),會(huì)自動(dòng)打開(kāi)商店,而不是開(kāi)啟重新返回 app;

這個(gè)時(shí)候需要做 2 個(gè)步驟:

  1. react-native-inappbrowser-reborn 改用 openAuth,它分三個(gè)參數(shù),第一個(gè)參數(shù)是原本的 url,第二個(gè)參數(shù)是 scheme 例如 my-app://,第三個(gè)參數(shù)就是關(guān)于瀏覽器的對(duì)象配置
  2. 進(jìn)入 adjust 后臺(tái),打開(kāi)「深度鏈接生成器」, 然后重新按照之前的配置重新配置一條深度鏈接,但在選擇 FORMAT 但時(shí)候,請(qǐng)選擇 jsr,簡(jiǎn)單描述一下 jsr 的類(lèi)型,它通用鏈接會(huì)在這種情況下中斷并將所有用戶發(fā)送到商店,即使他們安裝了該應(yīng)用程序也是如此。關(guān)于jsr可以看這里
  3. 如果原本使用了自定義域名 xxx.go.link,這個(gè)時(shí)候會(huì)消失,打開(kāi)鏈接會(huì)是 404,這個(gè)時(shí)候把它換成原本的「通用鏈接」xxx.adj.st 就可以了,當(dāng)然也可以自己進(jìn)行組合,格式如下
`https://app.adjust.com/jsr?url=${encodeURIComponent(https://xxx.adj.st?adj_t=xxxx&adjust_deeplink_js=1&${'其他的參數(shù)'}`)}`
  1. 當(dāng)再次使用 InAppBrowser.openAuth 的時(shí)候,就可以在.then 的回調(diào)函數(shù)中,拿到{type: 'success', url: 'my-app://?xxxx'}的內(nèi)容,然后就可以根據(jù)自己的業(yè)務(wù)需求進(jìn)行編碼了

注意:adjust_deeplink_js=1 是必須要加的參數(shù),不然還是會(huì)一樣開(kāi)啟商店 詳情可以查看 我的應(yīng)用已經(jīng)安裝,但為何還是會(huì)被轉(zhuǎn)到應(yīng)用商店?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容