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.
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];
}
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.
// 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);
}
}
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.
// 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
);
}
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
{
"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
- Navegue até o diretório raiz do seu projeto
- Crie uma pasta de plug-ins
- 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.
// 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.
// 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.
{
"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:
- O usuário clica em um link de rastreamento da Singular configurado com um valor de deep link
- O usuário instala e abre o aplicativo pela primeira vez
- O Singular SDK envia a primeira sessão para os servidores Singular
- A atribuição é concluída e identifica o deep link a partir do link de rastreamento
-
O valor do deep link retorna para o manipulador
withSingularLinkno parâmetrodeeplinkcomisDeferred = true
Testando Deep Links adiados:
- Desinstale o aplicativo do dispositivo de teste (se estiver instalado no momento)
- iOS: Redefinir seu IDFA. Android:Redefina seu ID de publicidade do Google (GAID)
- Clique no link de rastreamento Singular do dispositivo (verifique se ele está configurado com um valor de link profundo)
- 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:
- O usuário clica em um link de rastreamento Singular
- O sistema operacional fornece um URL aberto contendo todo o link de rastreamento Singular
- Durante a inicialização do SDK, a Singular analisa a URL
-
A Singular extrai os valores
deeplinkepassthrough -
Os valores retornam através do handler
withSingularLinkcomisDeferred = 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"