Skip to content

Vinculación en profundidad en las notificaciones push

Aprende a configurar notificaciones push silenciosas para el SDK de Braze.

Requisitos previos

Antes de poder utilizar esta característica, tendrás que integrar el SDK de Android Braze.

Crear un delegado universal

El SDK de Android ofrece la posibilidad de establecer un único objeto delegado para gestionar de forma personalizada todos los vínculos profundos abiertos por Braze a través de tarjetas de contenido, mensajes dentro de la aplicación y notificaciones push.

Tu objeto delegado debe implementar la interfaz IBrazeDeeplinkHandler y configurarse mediante la función BrazeDeeplinkHandler.setBrazeDeeplinkHandler(). En la mayoría de los casos, el delegado debe establecerse en la página Application.onCreate() de tu aplicación.

A continuación se muestra un ejemplo de anulación del comportamiento predeterminado UriAction con indicadores de intención personalizados y un comportamiento personalizado para las URL de YouTube:

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
34
35
36
37
38
public class CustomDeeplinkHandler implements IBrazeDeeplinkHandler {
  private static final String TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler.class);

  @Override
  public void gotoNewsFeed(Context context, NewsfeedAction newsfeedAction) {
    newsfeedAction.execute(context);
  }

  @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
31
32
33
34
class CustomDeeplinkHandler : IBrazeDeeplinkHandler {

  override fun gotoNewsFeed(context: Context, newsfeedAction: NewsfeedAction) {
    newsfeedAction.execute(context)
  }

  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)
  }
}

Vínculo profundo con la configuración de la aplicación

Para permitir que los vínculos profundos abran directamente la configuración de tu aplicación, necesitarás una página personalizada BrazeDeeplinkHandler. En el siguiente ejemplo, la presencia de un par clave-valor personalizado llamado open_notification_page hará que el vínculo profundo abra la página de configuración de la aplicación:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);
    }
  }

  @Override
  public void gotoNewsFeed(Context context, NewsfeedAction newsfeedAction) {}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)
    }
  }

  override fun gotoNewsFeed(context: Context, newsfeedAction: NewsfeedAction) {}
})

Establecimiento de vínculos profundos con el canal de noticias

Para establecer un vínculo profundo con el canal de noticias de Braze desde una notificación push, crea un vínculo profundo personalizado para tu actividad en el canal de noticias.

A continuación, cuando configures tu campaña de notificaciones push (ya sea a través del panel o de la API), configura la notificación para que navegue hasta el vínculo profundo de tu fuente de noticias.

Personalizar la actividad WebView

De forma predeterminada, cuando Braze abre vínculos profundos a sitios web dentro de la aplicación, los gestiona BrazeWebViewActivity. Para cambiar esto:

  1. Crea una nueva Actividad que maneje la URL de destino de Intent.getExtras() con la clave com.braze.Constants.BRAZE_WEBVIEW_URL_EXTRA. Para ver un ejemplo, consulta BrazeWebViewActivity.java.
  2. Añade esa actividad a AndroidManifest.xml y establece exported en false.
    1
    2
    3
    
     <activity
         android:name=".MyCustomWebViewActivity"
         android:exported="false" />
    
  3. Configura tu Actividad personalizada en un objeto constructor de BrazeConfig. Construye el constructor y pásalo a Braze.configure() en tu Application.onCreate().
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)

Usar Jetpack Compose

Para gestionar los enlaces profundos al utilizar Jetpack Compose con NavHost:

  1. Asegúrate de que la actividad que gestiona tu enlace profundo está registrada en el Manifiesto de 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. En NavHost, especifica qué enlaces profundos quieres que gestione.
    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. Dependiendo de la arquitectura de tu aplicación, puede que tengas que gestionar también la nueva intención que se envía a tu actividad actual.
    1
    2
    3
    4
    5
    6
    7
    
     DisposableEffect(Unit) {
         val listener = Consumer<Intent> {
             navHostController.handleDeepLink(it)
         }
         addOnNewIntentListener(listener)
         onDispose { removeOnNewIntentListener(listener) }
     }
    

Requisitos previos

Antes de poder utilizar esta característica, tendrás que integrar el SDK de Swift Braze.

Step 1: Register a scheme

To handle deep linking, a custom scheme must be stated in your Info.plist file. The navigation structure is defined by an array of dictionaries. Each of those dictionaries contains an array of strings.

Use Xcode to edit your Info.plist file:

  1. Add a new key, URL types. Xcode will automatically make this an array containing a dictionary called Item 0.
  2. Within Item 0, add a key URL identifier. Set the value to your custom scheme.
  3. Within Item 0, add a key URL Schemes. This will automatically be an array containing a Item 0 string.
  4. Set URL Schemes » Item 0 to your custom scheme.

Alternatively, if you wish to edit your Info.plist file directly, you can follow this spec:

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>

Step 2: Add a scheme allowlist

You must declare the URL schemes you wish to pass to canOpenURL(_:) by adding the LSApplicationQueriesSchemes key to your app’s Info.plist file. Attempting to call schemes outside this allowlist will cause the system to record an error in the device’s logs, and the deep link will not open. An example of this error will look like this:

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

For example, if an in-app message should open the Facebook app when tapped, the app has to have the Facebook custom scheme (fb) in your allowlist. Otherwise, the system will reject the deep link. Deep links that direct to a page or view inside your own app still require that your app’s custom scheme be listed in your app’s Info.plist.

Your example allowlist might look something like:

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

For more information, refer to Apple’s documentation on the LSApplicationQueriesSchemes key.

Step 3: Implement a handler

After activating your app, iOS will call the method application:openURL:options:. The important argument is the NSURL object.

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;
}

App Transport Security (ATS)

As defined by Apple, “App Transport Security is a feature that improves the security of connections between an app and web services. The feature consists of default connection requirements that conform to best practices for secure connections. Apps can override this default behavior and turn off transport security.”

ATS is applied by default. It requires that all connections use HTTPS and are encrypted using TLS 1.2 with forward secrecy. Refer to Requirements for Connecting Using ATS for more information. All images served by Braze to end devices are handled by a content delivery network (“CDN”) that supports TLS 1.2 and is compatible with ATS.

Unless they are specified as exceptions in your application’s Info.plist, connections that do not follow these requirements will fail with errors that are similar to the following.

Example Error 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."

Example Error 2:

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

ATS compliance is enforced for links opened within the mobile app (our default handling of clicked links) and does not apply to sites opened externally via a web browser.

Working with ATS

You can handle ATS in either of the following ways, but we recommend complying with ATS requirements.

Your Braze integration can satisfy ATS requirements by ensuring that any existing links you drive users to (for example, though in-app message and push campaigns) satisfy ATS requirements. While there are ways to bypass ATS restrictions, our recommendation is to ensure that all linked URLs are ATS-compliant. Given Apple’s increasing emphasis on application security, the following approaches to allowing ATS exceptions are not guaranteed to be supported by Apple.

You can allow a subset of links with certain domains or schemes to be treated as exceptions to the ATS rules. Your Braze integration will satisfy ATS requirements if every link you use in a Braze messaging channel is either ATS compliant or handled by an exception.

To add a domain as an exception of the ATS, add following to your app’s Info.plist file:

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>

Refer to Apple’s article on app transport security keys for more information.

You can turn off ATS entirely. Note that this is not recommended practice, due to both lost security protections and future iOS compatibility. To disable ATS, insert the following in your app’s Info.plist file:

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

Decoding URLs

The SDK percent-encodes links to create valid URLs. All link characters that are not allowed in a properly formed URL, such as Unicode characters, will be percent escaped.

To decode an encoded link, use the String property removingPercentEncoding. You must also return true in the BrazeDelegate.braze(_:shouldOpenURL:). A call to action is required to trigger the handling of the URL by your app. For example:

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;
}

Deep linking to app settings

You can take advantage of UIApplicationOpenSettingsURLString to deep link users to your app’s settings from Braze push notifications and in-app messages.

To take users from your app into the iOS settings:

  1. First, make sure your application is set up for either scheme-based deep links or universal links.
  2. Decide on a URI for deep linking to the Settings page (for example, myapp://settings or https://www.braze.com/settings).
  3. If you are using custom scheme-based deep links, add the following code to your application:openURL:options: method:
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;
}

Customization options

Default WebView customization

The Braze.WebViewController class displays web URLs opened by the SDK, typically when “Open Web URL Inside App” is selected for a web deep link.

You can customize the Braze.WebViewController via the BrazeDelegate.braze(_:willPresentModalWithContext:) delegate method.

Linking handling customization

The BrazeDelegate protocol can be used to customize the handling of URLs such as deep links, web URLs, and universal links. To set the delegate during Braze initialization, set a delegate object on the Braze instance. Braze will then call your delegate’s implementation of shouldOpenURL before handling any URIs.

Braze supports universal links in push notifications, in-app messages, and Content Cards. To enable universal link support, configuration.forwardUniversalLinks must be set to true.

When enabled, Braze will forward universal links to your app’s AppDelegate via the application:continueUserActivity:restorationHandler: method.

Your application also needs to be set up to handle universal links. Refer to Apple’s documentation to ensure your application is configured correctly for universal links.

Examples

BrazeDelegate

Here’s an example using BrazeDelegate. For more information, see Braze Swift SDK reference.

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;
}

Requisitos previos

Antes de que puedas implementar la vinculación en profundidad en tu aplicación Flutter, tendrás que configurar la vinculación en profundidad en la capa nativa de Android o iOS.

Implementar la vinculación en profundidad

Paso 1: Configurar el manejo integrado de Flutter

  1. En tu proyecto Xcode, abre tu archivo Info.plist.
  2. Añade un nuevo par clave-valor.
  3. Configura la clave en FlutterDeepLinkingEnabled.
  4. Configura el tipo en Boolean.
  5. Configura el valor en YES. Archivo `Info.plist` de un proyecto de ejemplo con el par clave-valor añadido.
  1. En tu proyecto de Android Studio, abre tu archivo AndroidManifest.xml.
  2. Localiza .MainActivity en tus etiquetas activity.
  3. Dentro de la etiqueta activity, añade la siguiente etiqueta meta-data:
    1
    
     <meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
    

Paso 2: Reenvía los datos a la capa Dart (opcional)

Puedes utilizar la gestión de enlaces nativa, de origen o de terceros para casos de uso complejos, como enviar a un usuario a una ubicación concreta de tu aplicación o llamar a una función específica.

Ejemplo: Vinculación en profundidad a un diálogo de alerta

En primer lugar, se utiliza un canal de métodos en la capa nativa para reenviar los datos de la cadena URL del vínculo profundo a la capa 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())
    }
  }

}

A continuación, se utiliza una función de devolución de llamada en la capa Dart para mostrar un diálogo de alerta utilizando los datos de la cadena URL enviados anteriormente.

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();
            },
          ),
        ],
      );
    },
  );
}
¿QUÉ TAN ÚTIL FUE ESTA PÁGINA?
New Stuff!