Skip to content

プッシュ通知のディープリンク

Braze SDK のサイレントプッシュ通知を設定する方法について説明します。

前提条件

この機能を使用する前に、Android Braze SDKを統合する必要がある。

ユニバーサルデリゲートの作成

Android SDK は、コンテンツカード、アプリ内メッセージ、プッシュ通知にわたって Braze によって開かれたすべてのディープリンクをカスタム処理するように単一のデリゲートオブジェクトを設定する機能を提供しています。

デリゲートオブジェクトはIBrazeDeeplinkHandlerインターフェイスを実装し、BrazeDeeplinkHandler.setBrazeDeeplinkHandler()を使用して設定する必要があります。ほとんどの場合、デリゲートはアプリのApplication.onCreate()で設定する必要があります。

以下は、カスタムインテントフラグと YouTube URL のカスタム動作でデフォルトのUriAction動作を上書きする例です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class CustomDeeplinkHandler implements IBrazeDeeplinkHandler {
  private static final String TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler.class);

  @Override
  public void gotoUri(Context context, UriAction uriAction) {
    String uri = uriAction.getUri().toString();
    // Open YouTube URLs in the YouTube app and not our app
    if (!StringUtils.isNullOrBlank(uri) && uri.contains("youtube.com")) {
      uriAction.setUseWebView(false);
    }

    CustomUriAction customUriAction = new CustomUriAction(uriAction);
    customUriAction.execute(context);
  }

  public static class CustomUriAction extends UriAction {

    public CustomUriAction(@NonNull UriAction uriAction) {
      super(uriAction);
    }

    @Override
    protected void openUriWithActionView(Context context, Uri uri, Bundle extras) {
      Intent intent = getActionViewIntent(context, uri, extras);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
      if (intent.resolveActivity(context.getPackageManager()) != null) {
        context.startActivity(intent);
      } else {
        BrazeLogger.w(TAG, "Could not find appropriate activity to open for deep link " + uri + ".");
      }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class CustomDeeplinkHandler : IBrazeDeeplinkHandler {

  override fun gotoUri(context: Context, uriAction: UriAction) {
    val uri = uriAction.uri.toString()
    // Open YouTube URLs in the YouTube app and not our app
    if (!StringUtils.isNullOrBlank(uri) && uri.contains("youtube.com")) {
      uriAction.useWebView = false
    }

    val customUriAction = CustomUriAction(uriAction)
    customUriAction.execute(context)
  }

  class CustomUriAction(uriAction: UriAction) : UriAction(uriAction) {

    override fun openUriWithActionView(context: Context, uri: Uri, extras: Bundle) {
      val intent = getActionViewIntent(context, uri, extras)
      intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
      if (intent.resolveActivity(context.packageManager) != null) {
        context.startActivity(intent)
      } else {
        BrazeLogger.w(TAG, "Could not find appropriate activity to open for deep link $uri.")
      }
    }
  }

  companion object {
    private val TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler::class.java)
  }
}

アプリ設定へのディープリンク

ディープリンクでアプリの設定を直接開くようにするには、カスタムのBrazeDeeplinkHandlerが必要です。以下の例では、open_notification_pageと呼ばれるカスタムのキーと値のペアが存在すると、ディープリンクがアプリの設定ページを開きます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BrazeDeeplinkHandler.setBrazeDeeplinkHandler(new IBrazeDeeplinkHandler() {
  @Override
  public void gotoUri(Context context, UriAction uriAction) {
    final Bundle extras = uriAction.getExtras();
    if (extras.containsKey("open_notification_page")) {
      Intent intent = new Intent();
      intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

      //for Android 5-7
      intent.putExtra("app_package", context.getPackageName());
      intent.putExtra("app_uid", context.getApplicationInfo().uid);

      // for Android 8 and later
      intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName());
      context.startActivity(intent);
    }
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
BrazeDeeplinkHandler.setBrazeDeeplinkHandler(object : IBrazeDeeplinkHandler {
  override fun gotoUri(context: Context, uriAction: UriAction) {
    val extras = uriAction.extras
    if (extras.containsKey("open_notification_page")) {
      val intent = Intent()
      intent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
      intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK

      //for Android 5-7
      intent.putExtra("app_package", context.packageName)
      intent.putExtra("app_uid", context.applicationInfo.uid)

      // for Android 8 and later
      intent.putExtra("android.provider.extra.APP_PACKAGE", context.packageName)
      context.startActivity(intent)
    }
  }
})

WebView アクティビティのカスタマイズ

デフォルトでは、Braze によってアプリ内でウェブサイトのディープリンクが開かれると、BrazeWebViewActivityによって処理されます。これを変更するには、以下を行います。

  1. キーcom.braze.Constants.BRAZE_WEBVIEW_URL_EXTRAIntent.getExtras()から対象の URL を扱うアクティビティを新規作成します。例については、BrazeWebViewActivity.ktを参照してください。
  2. そのアクティビティをAndroidManifest.xmlに追加し、exportedfalseに設定します。
    1
    2
    3
    
     <activity
         android:name=".MyCustomWebViewActivity"
         android:exported="false" />
    
  3. カスタムアクティビティをBrazeConfigビルダーオブジェクトに設定します。ビルダーをビルドし、Application.onCreate()Braze.configure() に渡します。
1
2
3
4
5
BrazeConfig brazeConfig = new BrazeConfig.Builder()
    .setCustomWebViewActivityClass(MyCustomWebViewActivity::class)
    ...
    .build();
Braze.configure(this, brazeConfig);
1
2
3
4
5
val brazeConfig = BrazeConfig.Builder()
    .setCustomWebViewActivityClass(MyCustomWebViewActivity::class.java)
    ...
    .build()
Braze.configure(this, brazeConfig)

Jetpack Compose の使用

Jetpack Compose をNavHost で使用する場合にディープリンクを処理するには:

  1. ディープリンクを扱うアクティビティがAndroidマニフェストに登録されていることを確認します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
     <activity
       ...
       <intent-filter>
         <action android:name="android.intent.action.VIEW" />
         <category android:name="android.intent.category.BROWSABLE" />
         <category android:name="android.intent.category.DEFAULT" />
         <data
             android:host="articles"
             android:scheme="myapp" />
       </intent-filter>
     </activity>
    
  2. NavHost で、処理するディープリンクを指定します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
     composableWithCompositionLocal(
         route = "YOUR_ROUTE_HERE",
         deepLinks = listOf(navDeepLink {
             uriPattern = "myapp://articles/{${MainDestinations.ARTICLE_ID_KEY}}"
         }),
         arguments = listOf(
             navArgument(MainDestinations.ARTICLE_ID_KEY) {
                 type = NavType.LongType
             }
         ),
     ) { backStackEntry ->
         val arguments = requireNotNull(backStackEntry.arguments)
         val articleId = arguments.getLong(MainDestinations.ARTICLE_ID_KEY)
         ArticleDetail(
             articleId
         )
     }
    
  3. アプリアーキテクチャによっては、現在のアクティビティに送信された新しいインテントも処理する必要があります。
    1
    2
    3
    4
    5
    6
    7
    
     DisposableEffect(Unit) {
         val listener = Consumer<Intent> {
             navHostController.handleDeepLink(it)
         }
         addOnNewIntentListener(listener)
         onDispose { removeOnNewIntentListener(listener) }
     }
    

前提条件

この機能を使う前に、Swift Braze SDKを統合する必要がある。

ディープリンクを扱う

ステップ 1: スキームを登録する

ディープリンクを処理するには、Info.plist ファイルにカスタムスキームを記述する必要があります。ナビゲーション構造はディクショナリの配列によって定義されます。これらの各ディクショナリには、文字列の配列が含まれています。

Xcode を使用して Info.plist ファイルを編集します。

  1. 新しいキー URL types を追加します。Xcode では、これが自動的に Item 0 というディクショナリを含む配列になります。
  2. Item 0 内に、キー URL identifier を追加します。カスタムスキームに値を設定します。
  3. Item 0 内に、キー URL Schemes を追加します。これは、自動的に Item 0 文字列を含む配列になります。
  4. URL Schemes » Item 0 をカスタムスキームに設定します。

また、Info.plist ファイルを直接編集する場合は、次の仕様に従うこともできます。

1
2
3
4
5
6
7
8
9
10
11
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>YOUR.SCHEME</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>YOUR.SCHEME</string>
        </array>
    </dict>
</array>

ステップ 2:スキームの許可リストを追加する

LSApplicationQueriesSchemes キーをアプリの Info.plist ファイルに追加して、canOpenURL(_:) に渡す URL スキームを宣言する必要があります。この許可リストに含まれないスキームを呼び出そうとすると、デバイスのログにエラーが記録され、ディープリンクは開かれません。以下はこのエラーの例です。

1
<Warning>: -canOpenURL: failed for URL: "yourapp://deeplink" – error: "This app is not allowed to query for scheme yourapp"

たとえば、アプリ内メッセージをタップしたときに Facebook アプリが開かれるようにするには、アプリの許可リストに Facebook カスタムスキーム (fb) が含まれている必要があります。含まれていないと、ディープリンクが拒否されます。自分のアプリ内のページやビューに誘導するディープリンクでも、アプリのカスタムスキームがアプリの Info.plist に含まれている必要があります。

以下は許可リストの例です。

1
2
3
4
5
6
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>myapp</string>
    <string>fb</string>
    <string>twitter</string>
</array>

詳細については、LSApplicationQueriesSchemes キーに関する Apple のドキュメントを参照してください。

ステップ3: ハンドラの実装

アプリをアクティブにすると、iOS でメソッド application:openURL:options: が呼び出されます。重要な引数は NSURL オブジェクトです。

1
2
3
4
5
6
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
  let path = url.path
  let query = url.query
  // Insert your code here to take some action based upon the path and query.
  return true
}
1
2
3
4
5
6
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  NSString *path  = [url path];
  NSString *query = [url query];
  // Insert your code here to take some action based upon the path and query.
  return YES;
}

アプリ・トランスポート・セキュリティ(ATS)

アップルの定義によれば、「App Transport Securityは、アプリとWebサービス間の接続のセキュリティを向上させる機能である。この機能は、安全な接続のベストプラクティスに準拠したデフォルトの接続要件で構成されています。アプリでこのデフォルト動作を無効にして、トランスポートセキュリティを無効にできます。」

ATS はデフォルトで適用されます。すべての接続が HTTPS を使用し、TLS 1.2を使用して暗号化され、前方秘匿性が確保される必要があります。詳細については、ATS を使用して接続するための要件を参照してください。Braze によりエンドデバイスに提供されるすべての画像は、TLS 1.2をサポートし、ATS と互換性のあるコンテンツ配信ネットワーク (「CDN」) によって処理されます。

アプリケーションのInfo.plist で例外として指定されていない限り、これらの要件に従わない接続は、以下のようなエラーで失敗する。

エラー例 1:

1
2
CFNetwork SSLHandshake failed (-9801)
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred, and a secure connection to the server cannot be made."

エラー例2:

1
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

ATS コンプライアンスは、モバイルアプリ内で開かれたリンク (クリックされたリンクのデフォルト処理) に適用され、Web ブラウザーから外部で開かれたサイトには適用されません。

ATSとの連携

ATSは以下のいずれかの方法で処理できるが、ATSの要件に従うことを推奨する。

(アプリ内メッセージやプッシュキャンペーンなどから) ユーザーを誘導する既存のリンクが ATS の要件を満たすようにすることで、Braze 統合が ATS 要件を満たすことができます。ATS の制限を回避する方法はありますが、リンクされたすべての URL が ATS に準拠するようにすることをお勧めします。Apple がアプリケーションのセキュリティをこれまで以上に重視していることを考えると、ATS の例外を許可する以下のアプローチが Apple によってサポートされる保証はありません。

特定のドメインやスキームのリンクのサブセットを ATS ルールの例外として処理することを許可できます。Braze メッセージングチャネルで使用するすべてのリンクが ATS に準拠しているか、例外として処理されている場合、Braze 統合は ATS 要件を満たします。

ATS の例外としてドメインを追加するには、アプリの Info.plist ファイルに以下を追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSExceptionDomains</key>
    <dict>
        <key>example.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <false/>
            <key>NSIncludesSubdomains</key>
            <true/>
        </dict>
    </dict>
</dict>

詳細については、アプリトランスポートセキュリティのキーに関する Apple の記事を参照してください。

ATS を完全に無効にできます。ただし、セキュリティ保護が失われることと、将来の iOS との互換性の両方を考慮して、この処理は推奨されないことに注意してください。ATS を無効にするには、アプリの Info.plist ファイルに以下を挿入します。

1
2
3
4
5
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

URLを解読する

SDK では、有効な URL を作成するためにリンクをパーセントエンコードします。適切な形式の URL で使用できないリンク文字 (ユニコード文字など) は、すべてパーセントエスケープされます。

エンコードされたリンクをデコードするには、String プロパティ removingPercentEncoding を使用します。また、BrazeDelegate.braze(_:shouldOpenURL:)true を返す必要があります。アプリによる URL の処理をトリガーするには、アクションの呼び出しが必要です。例:

1
2
3
4
5
  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let urlString = url.absoluteString.removingPercentEncoding
    // Handle urlString
    return true
  }
1
2
3
4
5
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options {
  NSString *urlString = [url.absoluteString stringByRemovingPercentEncoding];
  // Handle urlString
  return YES;
}

アプリ設定へのディープリンク

UIApplicationOpenSettingsURLString 、ユーザーをBrazeのプッシュ通知やアプリ内メッセージからアプリの設定にディープリンクさせることができる。

ユーザーをアプリから iOS 設定に移動させる手順は以下のとおりです。

  1. まず、アプリケーションがスキームベースのディープリンクまたはユニバーサルリンク用に設定されていることを確認します。
  2. [設定] ページへのディープリンクの URI (myapp://settingshttps://www.braze.com/settings など) を決定します。
  3. カスタムスキームベースのディープリンクを使用している場合は、application:openURL:options: メソッドに次のコードを追加します。
1
2
3
4
5
6
7
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
  let path = url.path
  if (path == "settings") {
    UIApplication.shared.openURL(URL(string:UIApplication.openSettingsURLString)!)
  }
  return true
}
1
2
3
4
5
6
7
8
9
10
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  NSString *path  = [url path];
  if ([path isEqualToString:@"settings"]) {
    NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
    [[UIApplication sharedApplication] openURL:settingsURL];
  }
  return YES;
}

カスタマイズ・オプション

デフォルト WebView のカスタマイズ

一般的に Web ディープリンクに対して [アプリ内で Web URL を開く] が選択されている場合、Braze.WebViewController クラスには SDK によって開かれる Web URL が表示されます。

Braze.WebViewController は、BrazeDelegate.braze(_:willPresentModalWithContext:) デリゲートメソッドを使用してカスタマイズできます。

リンク処理のカスタマイズ

BrazeDelegate プロトコルを使用して、ディープリンク、Web URL、ユニバーサルリンクなどの URL の処理をカスタマイズできます。Braze の初期化中にデリゲートを設定するには、Braze インスタンスでデリゲートオブジェクトを設定します。その後、URI を処理する前に Braze で shouldOpenURL のデリゲートの実装が呼び出されます。

Braze では、プッシュ通知、アプリ内メッセージ、コンテンツカードでユニバーサルリンクがサポートされています。ユニバーサルリンクのサポートを有効にするには、configuration.forwardUniversalLinkstrue に設定する必要があります。

有効にすると、application:continueUserActivity:restorationHandler: メソッドを使用して Braze からアプリのAppDelegate にユニバーサルリンクが転送されます。

また、ユニバーサルリンクを処理するようアプリケーションを設定する必要があります。Apple のドキュメントを参照し、アプリケーションがユニバーサルリンクに関して正しく設定されていることを確認してください。

BrazeDelegate

BrazeDelegate を使った例だ。詳細は、Braze Swift SDKリファレンスを参照。

1
2
3
4
5
6
7
8
func braze(_ braze: Braze, shouldOpenURL context: Braze.URLContext) -> Bool {
  if context.url.host == "MY-DOMAIN.com" {
    // Custom handle link here
    return false
  }
  // Let Braze handle links otherwise
  return true
}
1
2
3
4
5
6
7
8
- (BOOL)braze:(Braze *)braze shouldOpenURL:(BRZURLContext *)context {
  if ([[context.url.host lowercaseString] isEqualToString:@"MY-DOMAIN.com"]) {
    // Custom handle link here
    return NO;
  }
  // Let Braze handle links otherwise
  return YES;
}

前提条件

ディープリンクをFlutterアプリに実装する前に、ネイティブのAndroidまたはiOSレイヤーでディープリンクを設定する必要があります。

ディープリンクの実装

ステップ 1:Flutter の組み込み処理を設定する

  1. Xcodeプロジェクトで、Info.plistファイルを開封します。
  2. 新しいキーと値のペアを追加します。
  3. キーを FlutterDeepLinkingEnabled に設定します。
  4. タイプをBooleanに設定します。
  5. 値をYESに設定します。 キーと値のペアが追加されたサンプル・プロジェクトの `Info.plist` ファイル。
  1. Android Studio プロジェクトで、AndroidManifest.xml ファイルを開封します。
  2. activity タグで .MainActivity を見つけます。
  3. activityタグ内に、次のmeta-dataタグを追加します:
    1
    
     <meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
    

ステップ2:データをDartレイヤーに転送する(オプション)

ネイティブ、ファーストパーティ、またはサードパーティのリンク処理を使用して、アプリ内の特定の場所にユーザーを送信したり、特定の機能を呼び出したりするなどの複雑な使用例に対応できます。

例: アラートダイアログへのディープリンク

まず、メソッドチャネルがネイティブ層で使用され、ディープリンクのURL文字列データをDart層に転送します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
extension AppDelegate {
  
  // Delegate method for handling custom scheme links.
  override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    forwardURL(url)
    return true
  }
  
  // Delegate method for handling universal links.
  override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
        let url = userActivity.webpageURL else {
      return false
    }
    forwardURL(url)
    return true
  }

  private func forwardURL(_ url: URL) {
    guard let controller: FlutterViewController = window?.rootViewController as? FlutterViewController else { return }
    let deepLinkChannel = FlutterMethodChannel(name: "deepLinkChannel", binaryMessenger: controller.binaryMessenger)
    deepLinkChannel.invokeMethod("receiveDeepLink", arguments: url.absoluteString)
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MainActivity : FlutterActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    handleDeepLink(intent)
  }

  override fun onNewIntent(intent: Intent) {
      super.onNewIntent(intent)
    handleDeepLink(intent)
  }

  private fun handleDeepLink(intent: Intent) {
    val binaryMessenger = flutterEngine?.dartExecutor?.binaryMessenger
    if (intent?.action == Intent.ACTION_VIEW && binaryMessenger != null) {
      MethodChannel(binaryMessenger, "deepLinkChannel")
        .invokeMethod("receivedDeepLink", intent?.data.toString())
    }
  }

}

次に、コールバック関数がDartレイヤーで使用され、以前に送信されたURL文字列データを使用してアラートダイアログを表示します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MethodChannel('deepLinkChannel').setMethodCallHandler((call) async {
  deepLinkAlert(call.arguments, context);
});

void deepLinkAlert(String link, BuildContext context) {
  showDialog(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text("Deep Link Alert"),
        content: Text("Opened with deep link: $link"),
        actions: <Widget>[
          TextButton(
            child: Text("Close"),
            onPressed: () {
              Navigator.of(context).pop();
            },
          ),
        ],
      );
    },
  );
}
New Stuff!