diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 4a29b1ee8..3dee3fe97 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -286,7 +286,7 @@ PODS: - React - RNGestureHandler (2.3.0): - React-Core - - segment-analytics-react-native (2.1.11): + - segment-analytics-react-native (2.1.12): - React-Core - sovran-react-native - segment-analytics-react-native-plugin-idfa (0.2.1): @@ -459,7 +459,7 @@ SPEC CHECKSUMS: RNCAsyncStorage: b49b4e38a1548d03b74b30e558a1d18465b94be7 RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489 RNGestureHandler: 77d59828d40838c9fabb76a12d2d0a80c006906f - segment-analytics-react-native: b251b9e7277a8cbf0a8379d9bca155f0aa1d5d4a + segment-analytics-react-native: 5287504fa5aa60e64dbb497bee5c7eb6f94e5e49 segment-analytics-react-native-plugin-idfa: 80e5d610f537156833eabea12a1804523355de95 sovran-react-native: ef02f663b489ac5e63ea7b80cd8426bf82992263 Yoga: 90dcd029e45d8a7c1ff059e8b3c6612ff409061a diff --git a/packages/plugins/plugin-adjust/src/AdjustPlugin.tsx b/packages/plugins/plugin-adjust/src/AdjustPlugin.tsx index f0b3dba13..f029e8b67 100644 --- a/packages/plugins/plugin-adjust/src/AdjustPlugin.tsx +++ b/packages/plugins/plugin-adjust/src/AdjustPlugin.tsx @@ -17,7 +17,7 @@ export class AdjustPlugin extends DestinationPlugin { key = 'Adjust'; private settings: SegmentAdjustSettings | null = null; - private hasRegisteredCallback: Boolean = false; + private hasRegisteredCallback: boolean = false; update(settings: SegmentAPISettings, _: UpdateType) { const adjustSettings = settings.integrations[ diff --git a/packages/plugins/plugin-appsflyer/src/AppsflyerPlugin.tsx b/packages/plugins/plugin-appsflyer/src/AppsflyerPlugin.tsx index ed722d917..ac905f77b 100644 --- a/packages/plugins/plugin-appsflyer/src/AppsflyerPlugin.tsx +++ b/packages/plugins/plugin-appsflyer/src/AppsflyerPlugin.tsx @@ -3,27 +3,65 @@ import { IdentifyEventType, PluginType, TrackEventType, + UpdateType, + SegmentAPISettings, } from '@segment/analytics-react-native'; -import appsFlyer, { InitSDKOptions } from 'react-native-appsflyer'; +import type { SegmentAppsflyerSettings } from './types'; +import appsFlyer from 'react-native-appsflyer'; import identify from './methods/identify'; import track from './methods/track'; export class AppsflyerPlugin extends DestinationPlugin { type = PluginType.destination; - key = 'Appsflyer'; + key = 'AppsFlyer'; - constructor(opts: InitSDKOptions) { - super(); + private settings: SegmentAppsflyerSettings | null = null; + private hasRegisteredInstallCallback: boolean = false; + private hasRegisteredDeepLinkCallback: boolean = false; + private hasInitialized: boolean = false; - const defaultOpts = { - isDebug: true, + update(settings: SegmentAPISettings, _: UpdateType) { + let defaultOpts = { + isDebug: false, timeToWaitForATTUserAuthorization: 60, + onInstallConversionDataListener: true, }; - appsFlyer.initSdk({ - ...defaultOpts, - ...opts, - }); + const appsflyerSettings = settings.integrations[ + this.key + ] as SegmentAppsflyerSettings; + + if (appsflyerSettings === undefined) { + return; + } + const clientConfig = this.analytics?.getConfig(); + + this.settings = appsflyerSettings; + + if ( + this.settings.trackAttributionData && + !this.hasRegisteredInstallCallback + ) { + this.registerConversionCallback(); + this.hasRegisteredInstallCallback = true; + } + + if ( + clientConfig?.trackDeepLinks === true && + !this.hasRegisteredDeepLinkCallback + ) { + this.registerDeepLinkCallback(); + this.hasRegisteredDeepLinkCallback = true; + } + if (!this.hasInitialized) { + appsFlyer.initSdk({ + devKey: this.settings.appsFlyerDevKey, + appId: this.settings.appleAppID, + onDeepLinkListener: clientConfig?.trackDeepLinks === true, + ...defaultOpts, + }); + this.hasInitialized = true; + } } identify(event: IdentifyEventType) { @@ -35,4 +73,41 @@ export class AppsflyerPlugin extends DestinationPlugin { track(event); return event; } + + registerConversionCallback = () => { + appsFlyer.onInstallConversionData((res) => { + const { af_status, media_source, campaign, is_first_launch } = res?.data; + const properties = { + provider: this.key, + campaign: { + source: media_source, + name: campaign, + }, + }; + + if (is_first_launch === 'true') { + if (af_status === 'Non-organic') { + this.analytics?.track('Install Attributed', properties); + } else { + this.analytics?.track('Organic Install', { provider: 'AppsFlyer' }); + } + } + }); + }; + + registerDeepLinkCallback = () => { + appsFlyer.onAppOpenAttribution((res) => { + if (res?.status === 'success') { + const { campaign, media_source } = res.data; + const properties = { + provider: this.key, + campaign: { + name: campaign, + source: media_source, + }, + }; + this.analytics?.track('Deep Link Opened', properties); + } + }); + }; } diff --git a/packages/plugins/plugin-appsflyer/src/types.ts b/packages/plugins/plugin-appsflyer/src/types.ts new file mode 100644 index 000000000..6a6544e58 --- /dev/null +++ b/packages/plugins/plugin-appsflyer/src/types.ts @@ -0,0 +1,9 @@ +export type SegmentAppsflyerSettings = { + appleAppID?: string; + appsFlyerDevKey: string; + httpFallback: boolean; + rokuAppID?: string; + trackAttributionData: boolean; + type: string; + versionSettings: { [key: string]: string[] }; +};