SDK do React Native - Suporte a Deep Links

Documento

Adição de suporte para Deep Linking

Os links direcionam os usuários para um conteúdo específico dentro do seu aplicativo. Quando os utilizadores tocam numa ligação direta num dispositivo com a sua aplicação instalada, a aplicação abre-se diretamente para o conteúdo pretendido, como uma página de produto ou uma experiência específica.

Os links de rastreamento singulares suportam links diretos padrão (para aplicativos instalados) e links diretos adiados (para novas instalações). Para obter informações abrangentes, consulte as Perguntas frequentes sobre links diretos e as Perguntas frequentes sobre links singulares.

Caminhos de implementação: Este guia cobre ambas as implementações Bare React Native e Expo. A distinção existe porque a Expo abstrai o gerenciamento de código nativo para simplificar o desenvolvimento do React Native, enquanto o bare React Native fornece acesso direto às pastas nativas do iOS e Android. Escolha o caminho de implementação apropriado para a estrutura do seu projeto.


Requisitos

Pré-requisitos

Preencha os Pré-requisitos de Links Singulares para habilitar o deep linking para seu aplicativo.

Observações:

  • Este artigo assume que sua organização está usando Singular Links - a tecnologia de link de rastreamento da Singular. Os clientes mais antigos podem estar usando links de rastreamento herdados.
  • Os destinos de links diretos do seu aplicativo precisam ser configurados na página Aplicativos no Singular (consulte Configurando seu aplicativo para rastreamento de atribuição).

Implementar o manipulador de links do Singular

O manipulador SingularLink fornece um mecanismo de retorno de chamada para recuperar o deep link, o deferred deep link, os parâmetros de passagem e os parâmetros de URL dos links de rastreamento Singular quando o aplicativo é aberto.

Parâmetros disponíveis:

  • Deep Link (_dl): O URL de destino dentro do seu aplicativo para usuários que clicam no link
  • Deep Link diferido (_ddl): O URL de destino para os utilizadores que instalam a aplicação depois de clicarem no link
  • Passthrough (_p): Dados personalizados passados através do link de rastreamento para contexto adicional
  • urlParameters: Objeto de parâmetros de consulta de um Link Singular que abriu o aplicativo. Não aplicável a cenários de Deferred Deep Link.

Implementação nativa do React simples

Configure o deep linking para projetos React Native simples com acesso direto ao código nativo do iOS e do Android.

Configuração da plataforma iOS

Atualizar iOS AppDelegate

Habilite o SDK Singular para processar dados relacionados ao lançamento e manipular links diretos passando os objetos launchOptions e userActivity para o SDK Singular em seu arquivo AppDelegate.

AppDelegate.swiftAppDelegate.mm
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
    )
}

Esses métodos garantem que o SDK Singular receba informações críticas sobre como e por que seu aplicativo foi iniciado, que o Singular usa para rastreamento de atribuição e navegação de link profundo.


Configuração da plataforma Android

Atualizar a MainActivity do Android

Habilite o SDK Singular para processar dados relacionados à inicialização e manipular links profundos modificando o arquivo MainActivity para passar o objeto Intent para o SDK Singular.

MainActivity.ktMainActivity.java
// 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)
    }
}

O objeto Intent contém informações sobre como e por que seu aplicativo foi lançado, que o Singular usa para rastreamento de atribuição e navegação de link profundo.


Configuração do SDK

Adicionar retorno de chamada withSingularLink

Configure o retorno de chamada withSingularLink para manipular dados de deep link recebidos e dados de deep link adiados durante a inicialização do SDK.

New ArchitectureOld Architecture
// 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
  );
}

Observação: O retorno de chamada withSingularLink é acionado somente quando o aplicativo é aberto por meio de um Singular Link. Para obter mais informações, consulte as Perguntas frequentes sobre links singulares.

Para obter a documentação completa do método, consulte a referência withSingularLink.


Implementação da Expo

Configure o deep linking para projetos Expo usando arquivos de configuração e plug-ins personalizados para lidar com modificações de código nativo automaticamente.

Configuração do aplicativo

Atualizar App.json para Deep Linking

Devido à abstração da Expo do gerenciamento de código nativo, adicione configurações de deep linking específicas da plataforma no arquivo app.json.

Configuração do iOS

  • Adicione o recurso associatedDomains com seu domínio de link de rastreamento Singular
  • Adicione o esquema para suporte de esquema de URL personalizado (fallback)

Configuração do Android

  • Actualize o valor do anfitrião para corresponder ao seu domínio de ligações Singular (por exemplo, example.sng.link)
  • Configurar o esquema no filtro de última intenção para suporte de ligação de esquema tradicional
app.json
{
  "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"
          ]
        }
      ]
    }
  }
}

Plug-ins personalizados da Expo

Criar plug-ins específicos da plataforma

Esses arquivos de plug-in automatizam as modificações de código nativo necessárias para a integração do SDK Singular no fluxo de trabalho gerenciado da Expo, eliminando a necessidade de edição manual após cada expo prebuild.

  • Plug-in iOS: Detecta Swift ou Objective-C AppDelegate e injeta instruções de importação, registro de identificador de dispositivo e código de manipulação de link universal que encaminha dados de atribuição para a ponte Singular React Native
  • Plug-in do Android: Detecta arquivos Java ou Kotlin MainActivity e adiciona importações, registro de intenção e o método onNewIntentque captura links diretos e os passa para o módulo de ponte do Singular

Ambos os plug-ins garantem que, quando os usuários tocam em deep links ou links universais, o código nativo captura e encaminha adequadamente esses dados para seus hooks do React Native para rastreamento de atribuição.

Etapas de configuração

  1. Navegue até o diretório raiz do seu projeto
  2. Crie uma pasta de plug-ins
  3. Adicione os seguintes arquivos de plug-in JavaScript

Implementação do plug-in do iOS

Crie plugins/singular-ios-deeplink-plugin.js para configurar automaticamente o iOS deep linking.

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;

Implementação do plug-in do Android

Crie plugins/singular-android-deeplink-plugin.js para configurar automaticamente o Android deep linking.

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;

Registar plug-ins

Atualize seu arquivo app.json para incluir os novos plug-ins personalizados juntamente com o plug-in Singular React Native.

app.json
{
  "expo": {
    "plugins": [
      "singular-react-native",
      "./plugins/singular-ios-deeplink-plugin.js",
      "./plugins/singular-android-deeplink-plugin.js"
    ]
  }
}

Configuração de SDK para Expo

Configure o retorno de chamada withSingularLink em seu aplicativo Expo usando a mesma abordagem do React Native simples. Consulte a seção Configuração do SDK acima para obter detalhes completos de implementação.


Comportamento do manipulador

Entendendo a resolução de links

O manipulador withSingularLink se comporta de maneira diferente, dependendo se o aplicativo foi instalado recentemente ou já está instalado.

Instalação recente (Deep Link diferido)

Em uma instalação recente, não existe nenhuma URL aberta quando o aplicativo é iniciado. A Singular completa a atribuição para determinar se o link de rastreamento contém um valor de deep link ou de deferred deep link.

Fluxo de Deep Link Diferido:

  1. O usuário clica em um link de rastreamento da Singular configurado com um valor de deep link
  2. O usuário instala e abre o aplicativo pela primeira vez
  3. O Singular SDK envia a primeira sessão para os servidores Singular
  4. A atribuição é concluída e identifica o deep link a partir do link de rastreamento
  5. O valor do deep link retorna para o manipulador withSingularLink no parâmetro deeplink com isDeferred = true

Testando Deep Links adiados:

  1. Desinstale o aplicativo do dispositivo de teste (se estiver instalado no momento)
  2. iOS: Redefinir seu IDFA. Android:Redefina seu ID de publicidade do Google (GAID)
  3. Clique no link de rastreamento Singular do dispositivo (verifique se ele está configurado com um valor de link profundo)
  4. Instale e abra o aplicativo

A atribuição deve ser concluída com êxito e o valor diferido do deep link será passado para o manipulador withSingularLink.

Dica profissional: Ao testar links diretos com uma compilação de desenvolvimento usando um nome de pacote diferente (por exemplo, com.example.devem vez de com.example.prod), configure o link de rastreamento especificamente para o nome do pacote do aplicativo de desenvolvimento. Depois de clicar no link de teste, instale a compilação de desenvolvimento diretamente no dispositivo (através do Android Studio ou Xcode) em vez de transferir a aplicação de produção a partir da loja de aplicações.


Já instalado (ligação direta imediata)

Quando o aplicativo já está instalado, clicar em um link singular abre o aplicativo imediatamente usando a tecnologia Universal Links (iOS) ou Android App Links.

Fluxo imediato do Deep Link:

  1. O usuário clica em um link de rastreamento Singular
  2. O sistema operacional fornece um URL aberto contendo todo o link de rastreamento Singular
  3. Durante a inicialização do SDK, a Singular analisa a URL
  4. A Singular extrai os valores deeplink e passthrough
  5. Os valores retornam através do handler withSingularLink com isDeferred = false

Recursos avançados

Parâmetros de passagem

Capture dados adicionais do clique do link de rastreamento usando parâmetros de passagem.

Se um parâmetro passthrough (_p) for incluído no link de rastreamento, o parâmetro passthroughdo manipulador withSingularLink conterá os dados correspondentes. Utilize-o para capturar metadados de campanha, dados de segmentação de utilizadores ou qualquer informação personalizada de que necessite na aplicação.

Exemplo de valor de passagem:

{
  "utm_source": "Facebook",
  "utm_campaign": "Web-to-App US RON",
  "utm_creative": "Blue CTA for Promo",
  "promo_code": "45k435hg"
}

Exemplo de link de rastreamento com valor de passagem codificado por URL no parâmetro (_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

O manipulador withSingularLink receberá:

urlParameters = {"utm_source": "Facebook","utm_campaign": "Web-to-App US RON","utm_creative": "Blue CTA for Promo","promo_code": "45k435hg"}

Encaminhar todos os parâmetros de consulta

Capture todos os parâmetros de consulta do URL do link de rastreamento anexando o parâmetro _forward_params=2 ao seu link de rastreamento.

Quando _forward_params=2 é adicionado ao link de rastreamento, todos os parâmetros de consulta são incluídos no parâmetro deeplink do manipulador withSingularLink, dando-lhe acesso ao URL completo com todos os seus parâmetros.

Exemplo de link de rastreamento:

https://yourapp.sng.link/A1b2c/abc123?_dl=myapp://product/123&_forward_params=2&utm_source=facebook&promo=SALE2024

O manipulador withSingularLink receberá:

deeplink = "myapp://product/123?utm_source=facebook&promo=SALE2024"