ディープリンクサポートの追加
ディープリンクは、ユーザーをアプリ内の特定のコンテンツに誘導します。アプリがインストールされたデバイスでユーザーがディープリンクをタップすると、アプリは製品ページや特定の体験など、目的のコンテンツに直接開きます。
Singularトラッキングリンクは、標準ディープリンク(インストール済みアプリ用)とディファードディープリンク(新規インストール用)の両方をサポートしています。包括的な情報については、ディープリンクFAQと シンギュラーリンクFAQを参照してください。
実装パス:このガイドでは、ベアReact Nativeと Expoの両方の実装をカバーします。ExpoはReact Native開発を簡素化するためにネイティブコード管理を抽象化する一方、ベアReact NativeはネイティブiOSおよびAndroidフォルダへの直接アクセスを提供するため、この区別が存在します。プロジェクトの構造に合わせて適切な実装パスを選択してください。
必要条件
前提条件
アプリのディープリンクを有効にするには、Singular Links Prerequisitesを完了してください。
注意事項
- この記事では、Singular Links(Singularのトラッキングリンク技術)を使用していることを前提としています。古いお客様は従来のトラッキングリンクを使用している可能性があります。
- アプリのディープリンク先をSingularのアプリページで設定する必要があります(アトリビューショントラッキングのためのアプリの設定を参照)。
Singularリンクハンドラの実装
SingularLink ハンドラーは、アプリが開いたときに Singular トラッキングリンクからディープリンク、ディファードディープリンク、パススルーパラメータ、URL パラメータを取得するコールバックメカニズムを提供します。
利用可能なパラメータ
- ディープリンク (_dl):リンクをクリックしたユーザーのアプリ内のリンク先URL
- ディファードディープリンク(_ddl):リンクをクリックした後にアプリをインストールしたユーザーのリンク先URL
- パススルー (_p):追加コンテキストのためにトラッキングリンクを通して渡されるカスタムデータ
- urlParameters:アプリを開いたSingularリンクからのクエリパラメータのオブジェクト。Deferred Deep Linkシナリオには適用されません。
ベアReact Nativeの実装
iOSとAndroidのネイティブコードに直接アクセスできるベアReact Nativeプロジェクト用にディープリンクを設定します。
iOSプラットフォームの構成
iOS AppDelegateの更新
AppDelegate ファイルでlaunchOptions とuserActivity オブジェクトを Singular SDK に渡すことで、Singular SDK が起動関連データを処理し、ディープリンクを処理できるようにします。
import Singular
// Deferred Deep link and Short Link support
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
if let singularBridge = NSClassFromString("SingularBridge") {
let selector = NSSelectorFromString("startSessionWithLaunchOptions:")
if singularBridge.responds(to: selector) {
singularBridge.perform(selector, with: launchOptions, afterDelay: 1)
}
}
factory.startReactNative(
withModuleName: "AppName", // Update with your App Name
in: window,
launchOptions: launchOptions
)
return true
}
// Universal Links (NSUserActivity)
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if let url = userActivity.webpageURL {
print("Singular: Universal link received:", url.absoluteString)
if let singularBridge = NSClassFromString("SingularBridge") {
let selector = NSSelectorFromString("startSessionWithUserActivity:")
if singularBridge.responds(to: selector) {
singularBridge.perform(selector, with: userActivity, afterDelay: 1)
}
}
}
return RCTLinkingManager.application(
application,
continue: userActivity,
restorationHandler: restorationHandler
)
}
// Top of the AppDelegate.mm
#import <React/RCTLinkingManager.h>
#import <Singular-React-Native/SingularBridge.h>
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add inside didFinishLaunchingWithOptions
[SingularBridge startSessionWithLaunchOptions:launchOptions];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
[SingularBridge startSessionWithUserActivity:userActivity];
return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}
これらのメソッドは、Singular SDKがアプリの起動方法と起動理由に関する重要な情報を受け取れるようにし、Singularがアトリビューショントラッキングとディープリンクナビゲーションに使用します。
Androidプラットフォームの設定
AndroidのMainActivityを更新する
MainActivity ファイルを修正してIntent オブジェクトを Singular SDK に渡すことで、Singular SDK が起動関連データを処理し、ディープリンクを処理できるようにします。
// Add as part of the imports at the top of the class
import android.content.Intent
import net.singular.react_native.SingularBridgeModule
// Add to the MainActivity class
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.data != null) {
setIntent(intent)
SingularBridgeModule.onNewIntent(intent)
}
}
// Add as part of the imports at the top of the class
import android.content.Intent;
import net.singular.react_native.SingularBridgeModule;
// Add to the MainActivity class
@Override
public void onNewIntent(Intent intent) {
if(intent.getData() != null) {
setIntent(intent);
super.onNewIntent(intent);
SingularBridgeModule.onNewIntent(intent);
}
}
Intent オブジェクトには、アプリの起動方法と起動理由に関する情報が含まれており、Singular はこれをアトリビューショントラッキングとディープリンクナビゲーションに使用します。
SDKの設定
withSingularLinkコールバックを追加する
SDKの初期化中に、入力されるディープリンクと遅延ディープリンクデータを処理するために、withSingularLink コールバックを設定します。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import { NativeEventEmitter } from 'react-native';
function App() {
useEffect(() => {
// Create config
const config: SingularConfig = {
apikey: 'YOUR_SDK_KEY',
secret: 'YOUR_SDK_SECRET'
// Note: Deep link handlers are NOT set in config for TurboModule
};
NativeSingular.init(config);
// Set up deep link handler using event emitter
const emitter = new NativeEventEmitter(NativeSingular);
const linkListener = emitter.addListener('SingularLinkHandler', (params) => {
console.log('Singular Link resolved:', params);
// Extract deep link parameters
console.log('Singular: Deep link received:', params.deeplink);
console.log('Singular: Passthrough data:', params.passthrough);
console.log('Singular: Is deferred:', params.isDeferred);
console.log('Singular: urlParameters:', params.urlParameters);
// Handle deep link navigation
});
// Clean up event listener on unmount
return () => {
linkListener.remove();
};
}, []);
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular, SingularConfig } from 'singular-react-native';
function App() {
useEffect(() => {
const config = new SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
)
.withSingularLink((params) => {
console.log('Singular Link resolved:', params);
// Extract deep link parameters
console.log('Singular: Deep link received:', params.deeplink);
console.log('Singular: Passthrough data:', params.passthrough);
console.log('Singular: Is deferred:', params.isDeferred);
console.log('Singular: urlParameters:', params.urlParameters);
// Handle deep link navigation
});
// Initialize SDK
Singular.init(config);
}, []);
return (
// Your app components
);
}
注意: withSingularLink コールバックは、アプリがシンギュラーリンクを介して開いた場合にのみトリガーされます。詳細については、Singular Links FAQを参照してください。
メソッドの完全なドキュメントについては、withSingularLinkリファレンスを参照してください。
Expo の実装
設定ファイルとカスタムプラグインを使用して Expo プロジェクトのディープリンクを設定し、ネイティブコードの変更を自動的に処理します。
アプリの構成
ディープリンク用にApp.jsonを更新する
Expo はネイティブ コード管理から抽象化されているため、app.json ファイルにプラットフォーム固有のディープ リンク設定を追加します。
iOS 構成
- SingularトラッキングリンクのドメインでassociatedDomains機能を追加します。
- カスタムURLスキームのサポート(フォールバック)のためにスキームを追加します。
Androidの設定
-
ホスト値をあなたのSingular Linksドメインと一致するように更新します(例:
example.sng.link)。 - 従来のスキームリンクをサポートするために、最後のインテント フィルターでスキームを設定する
{
"expo": {
"name": "MySimpleApp",
"slug": "mysimpleapp",
"scheme": "ios-app-scheme",
"ios": {
"associatedDomains": [
"applinks:example.sng.link"
]
},
"android": {
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "http",
"host": "example.sng.link",
"pathPrefix": "/A"
},
{
"scheme": "https",
"host": "example.sng.link",
"pathPrefix": "/A"
},
{
"scheme": "http",
"host": "example.sng.link",
"pathPrefix": "/B"
},
{
"scheme": "https",
"host": "example.sng.link",
"pathPrefix": "/B"
},
{
"scheme": "http",
"host": "example.sng.link",
"pathPrefix": "/E"
},
{
"scheme": "https",
"host": "example.sng.link",
"pathPrefix": "/E"
},
{
"scheme": "http",
"host": "example.sng.link",
"pathPrefix": "/F"
},
{
"scheme": "https",
"host": "example.sng.link",
"pathPrefix": "/F"
}
],
"category": [
"BROWSABLE",
"DEFAULT"
]
},
{
"action": "VIEW",
"data": [
{
"scheme": "android-app-scheme"
}
],
"category": [
"BROWSABLE",
"DEFAULT"
]
}
]
}
}
}
カスタムExpoプラグイン
プラットフォーム固有のプラグインを作成する
これらのプラグインファイルは、Expoの管理されたワークフローでSingular SDKの統合に必要なネイティブコードの修正を自動化し、expo prebuild ごとに手動で編集する手間を省きます。
- iOSプラグイン:SwiftまたはObjective-CのAppDelegateを検出し、インポート文、デバイス識別子のロギング、アトリビューションデータをSingular React Nativeブリッジに転送するユニバーサルリンク処理コードを注入します。
-
Androidプラグイン:JavaまたはKotlinのMainActivityファイルを検出し、インポート、インテント・ロギング、ディープリンクをキャプチャしてSingularのブリッジモジュールに渡す
onNewIntentメソッドを追加する。
どちらのプラグインも、ユーザーがディープリンクやユニバーサルリンクをタップすると、ネイティブコードがそのデータを適切にキャプチャし、アトリビューショントラッキング用のReact Nativeフックに転送するようにします。
セットアップの手順
- プロジェクトのルート・ディレクトリに移動する
- pluginsフォルダを作成する
- 以下のJavaScriptプラグインファイルを追加します。
iOSプラグインの実装
iOSのディープリンクを自動的に設定するために、plugins/singular-ios-deeplink-plugin.js 。
// plugins/singular-ios-deeplink-plugin.js
const { withAppDelegate } = require('@expo/config-plugins');
const withUniversalSingular = (config) => {
return withAppDelegate(config, (config) => {
const { modResults } = config;
// Detect file type
const isSwift = modResults.contents.includes('import Expo') ||
modResults.contents.includes('class AppDelegate');
const isObjectiveC = modResults.contents.includes('#import') ||
modResults.contents.includes('@implementation');
console.log(`Detected AppDelegate type: ${isSwift ? 'Swift' : isObjectiveC ? 'Objective-C' : 'Unknown'}`);
if (isSwift) {
modResults.contents = applySwiftModifications(modResults.contents);
} else if (isObjectiveC) {
modResults.contents = applyObjectiveCModifications(modResults.contents);
} else {
console.warn('Unable to detect AppDelegate type - skipping Singular modifications');
}
return config;
});
};
function applySwiftModifications(contents) {
// Add Singular import
if (!contents.includes('import Singular')) {
contents = contents.replace(
/import ReactAppDependencyProvider/,
`import ReactAppDependencyProvider
import Singular`
);
}
// Add IDFV logging
const swiftLaunchPattern = /didFinishLaunchingWithOptions.*?\) -> Bool \{/s;
if (!contents.includes('UIDevice.current.identifierForVendor')) {
contents = contents.replace(
swiftLaunchPattern,
`didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
print("IDFV", UIDevice.current.identifierForVendor!.uuidString)`
);
}
// Add universal link handling
if (!contents.includes('Universal link received for Singular')) {
contents = contents.replace(
/continue userActivity: NSUserActivity,\s*restorationHandler: @escaping \(\[UIUserActivityRestoring\]\?\) -> Void\s*\) -> Bool \{/,
`continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
if let url = userActivity.webpageURL {
print("Universal link received for Singular:", url.absoluteString)
if let singularBridge = NSClassFromString("SingularBridge") {
let selector = NSSelectorFromString("startSessionWithUserActivity:")
if singularBridge.responds(to: selector) {
singularBridge.perform(selector, with: userActivity, afterDelay: 1)
}
}
}`
);
}
return contents;
}
function applyObjectiveCModifications(contents) {
// Add Singular import
if (!contents.includes('#import <Singular-React-Native/SingularBridge.h>')) {
contents = contents.replace(
/#import "AppDelegate.h"/,
`#import "AppDelegate.h"
#import <Singular-React-Native/SingularBridge.h>`
);
}
// Add IDFV logging to didFinishLaunchingWithOptions
const objcLaunchPattern = /- \(BOOL\)application:\(UIApplication \*\)application didFinishLaunchingWithOptions:\(NSDictionary \*\)launchOptions\s*{/;
if (!contents.includes('NSLog(@"IDFV %@"')) {
contents = contents.replace(
objcLaunchPattern,
`- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(@"IDFV %@", [UIDevice currentDevice].identifierForVendor.UUIDString);
[SingularBridge startSessionWithLaunchOptions:launchOptions];`
);
}
// Add continueUserActivity method if not present
if (!contents.includes('continueUserActivity')) {
const methodToAdd = `
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
NSLog(@"Universal link received for Singular: %@", userActivity.webpageURL.absoluteString);
[SingularBridge startSessionWithUserActivity:userActivity];
return YES;
}`;
contents = contents.replace(
/@end$/,
`${methodToAdd}
@end`
);
}
return contents;
}
module.exports = withUniversalSingular;
Androidプラグインの実装
Android のディープリンクを自動的に設定するため、plugins/singular-android-deeplink-plugin.js を作成します。
// plugins/singular-android-deeplink-plugin.js
const { withMainActivity } = require('@expo/config-plugins');
const withSingularAndroid = (config) => {
// Add MainActivity modifications
config = withMainActivity(config, (config) => {
const { modResults } = config;
// Detect Java vs Kotlin
const isKotlin = modResults.contents.includes('class MainActivity') &&
modResults.contents.includes('override fun');
const isJava = modResults.contents.includes('public class MainActivity') &&
modResults.contents.includes('@Override');
if (isKotlin) {
modResults.contents = applyKotlinModifications(modResults.contents);
} else if (isJava) {
modResults.contents = applyJavaModifications(modResults.contents);
}
return config;
});
return config;
};
function applyKotlinModifications(contents) {
// Add imports
if (!contents.includes('android.content.Intent')) {
contents = contents.replace(
/package com\..*?\n/,
`$&
import android.content.Intent
import net.singular.react_native.SingularBridgeModule`
);
}
// Add onNewIntent method
if (!contents.includes('onNewIntent')) {
const methodToAdd = `
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.data != null) {
setIntent(intent)
SingularBridgeModule.onNewIntent(intent)
}
}`;
contents = contents.replace(
/}\s*$/,
`${methodToAdd}
}`
);
}
return contents;
}
function applyJavaModifications(contents) {
// Add imports
if (!contents.includes('android.content.Intent')) {
contents = contents.replace(
/package com\..*?;/,
`$&
import android.content.Intent;
import net.singular.react_native.SingularBridgeModule;`
);
}
// Add onNewIntent method
if (!contents.includes('onNewIntent')) {
const methodToAdd = `
@Override
public void onNewIntent(Intent intent) {
if(intent.getData() != null) {
setIntent(intent);
super.onNewIntent(intent);
SingularBridgeModule.onNewIntent(intent);
}
}`;
contents = contents.replace(
/}\s*$/,
`${methodToAdd}
}`
);
}
return contents;
}
module.exports = withSingularAndroid;
プラグインの登録
app.json ファイルを更新して、Singular React Native プラグインと一緒に新しいカスタムプラグインを含めます。
{
"expo": {
"plugins": [
"singular-react-native",
"./plugins/singular-ios-deeplink-plugin.js",
"./plugins/singular-android-deeplink-plugin.js"
]
}
}
Expo 用 SDK の設定
素の React Native と同じアプローチを使用して、Expo アプリでwithSingularLink コールバックを構成します。完全な実装の詳細については、上記のSDK構成のセクションを参照してください。
ハンドラーの動作
リンク解決を理解する
withSingularLink ハンドラの動作は、アプリがインストールされたばかりか、既にインストールされているかによって異なります。
新規インストール (ディープリンクの遅延)
新規インストールでは、アプリの起動時に Open URL が存在しません。トラッキングリンクにディープリンクまたはディファードディープリンクの値が含まれているかどうかを判断するために、アトリビューションが完了します。
ディファードディープリンクのフロー
- ユーザーがディープリンク値で設定されたSingularトラッキングリンクをクリックする。
- ユーザーがアプリをインストールして初めて開く
- Singular SDKが最初のセッションをSingularサーバーに送信します。
- アトリビューションが完了し、トラッキングリンクからディープリンクが特定される
-
ディープリンクの値が、
isDeferred = trueのdeeplinkパラメータ内のwithSingularLinkハンドラに返される。
ディファードディープリンクのテスト
- テストデバイスからアプリをアンインストールする(現在インストールされている場合)
- iOS:IDFA をリセットします。Android:Google Advertising ID(GAID)をリセットします。
- デバイスからSingularトラッキングリンクをクリックする(ディープリンク値が設定されていることを確認する
- アプリをインストールして開く
アトリビューションが正常に完了し、延期されたディープリンク値がwithSingularLink ハンドラーに渡されます。
プロのヒント異なるパッケージ名(例えば、com.example.prod の代わりにcom.example.dev)を使用する開発ビルドでディープリンクをテストする場合、開発アプリのパッケージ名専用にトラッキングリンクを設定してください。テストリンクをクリックした後、アプリストアから本番アプリをダウンロードするのではなく、開発ビルドを直接デバイスにインストールします(Android Studio または Xcode 経由)。
インストール済み(即時ディープリンク)
アプリが既にインストールされている場合、ユニバーサルリンク(iOS)またはAndroid App Linksテクノロジーを使用して、シングラーリンクをクリックするとアプリがすぐに開きます。
即時ディープリンクフロー:
- ユーザーがSingularトラッキングリンクをクリック
- オペレーティングシステムは、Singularトラッキングリンク全体を含むOpen URLを提供します。
- SDKの初期化中にSingularがURLを解析します。
-
Singularは
deeplinkとpassthroughの値を抽出します。 -
値は
isDeferred = falseとともにwithSingularLinkハンドラーを通して返されます。
高度な機能
パススルーパラメータ
パススルーパラメータを使用して、トラッキングリンククリックから追加データを取得します。
passthrough (_p) パラメーターがトラッキングリンクに含まれている場合、withSingularLink ハンドラーのpassthroughパラメーターに対応するデータが含まれます。キャンペーンメタデータ、ユーザーセグメンテーションデータ、またはアプリで必要なカスタム情報の取得に使用します。
パススルー値の例
{
"utm_source": "Facebook",
"utm_campaign": "Web-to-App US RON",
"utm_creative": "Blue CTA for Promo",
"promo_code": "45k435hg"
}
URL エンコードされたパススルー値を (_p) パラメータに持つトラッキングリンクの例:
https://yourapp.sng.link/A1b2c/abc123?_dl=myapp://product/123&_p=%7B%22utm_source%22%3A%20%22Facebook%22%2C%22utm_campaign%22%3A%20%22Web-to-App%20US%20RON%22%2C%22utm_creative%22%3A%20%22Blue%20CTA%20for%20Promo%22%2C%22promo_code%22%3A%20%2245k435hg%22%7D
withSingularLink ハンドラーが受け取ります:
urlParameters = {"utm_source": "Facebook","utm_campaign": "Web-to-App US RON","utm_creative": "Blue CTA for Promo","promo_code": "45k435hg"}
すべてのクエリパラメータを転送
_forward_params=2 パラメータをトラッキングリンクに追加することで、トラッキングリンクの URL からすべてのクエリパラメータを取得します。
_forward_params=2 がトラッキングリンクに追加されると、すべてのクエリパラメータがwithSingularLink ハンドラのdeeplink パラメータに含まれ、すべてのパラメータを含む完全な URL にアクセスできるようになります。
トラッキングリンクの例:
https://yourapp.sng.link/A1b2c/abc123?_dl=myapp://product/123&_forward_params=2&utm_source=facebook&promo=SALE2024
withSingularLink ハンドラーが受信します:
deeplink = "myapp://product/123?utm_source=facebook&promo=SALE2024"