linktrail-react-native 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LinkTrail
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,28 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "LinktrailReactNative"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => "15.1" }
14
+ s.source = { :git => "https://github.com/linktrail-io/react-native.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
17
+ s.swift_version = "5.9"
18
+
19
+ # The binary LinkTrail iOS SDK, published to the CocoaPods trunk — CocoaPods
20
+ # resolves it automatically, so the consuming app needs no extra Podfile line.
21
+ s.dependency "LinkTrailSDK", "~> 0.0.8"
22
+
23
+ s.pod_target_xcconfig = {
24
+ "DEFINES_MODULE" => "YES",
25
+ }
26
+
27
+ install_modules_dependencies(s)
28
+ end
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # LinkTrail React Native SDK
2
+
3
+ Mobile attribution and deferred deep linking for React Native. A New Architecture **TurboModule**
4
+ that wraps the native [LinkTrail iOS](https://github.com/linktrail-io/ios-sdk) and
5
+ [Android](https://github.com/linktrail-io/android-sdk) SDKs — the API type is `LinkTrail`.
6
+
7
+ Requires React Native **0.76+** (New Architecture, the default), iOS **15.1+**, Android **minSdk 26+**.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ npm install linktrail-react-native
13
+ ```
14
+
15
+ The native SDKs resolve automatically — `LinkTrailSDK` from the CocoaPods trunk, `io.linktrail:sdk`
16
+ from Maven Central — so there's nothing else to add.
17
+
18
+ - **iOS** — `cd ios && pod install`.
19
+ - **Android** — nothing extra; just make sure your app's `minSdkVersion` is **26** or higher.
20
+ - **Expo** — needs a [development build](https://docs.expo.dev/develop/development-builds/introduction/) (not Expo Go). Set `minSdkVersion` via [`expo-build-properties`](https://docs.expo.dev/versions/latest/sdk/build-properties/), then `npx expo prebuild`:
21
+
22
+ ```json
23
+ { "expo": { "plugins": [["expo-build-properties", { "android": { "minSdkVersion": 26 } }]] } }
24
+ ```
25
+
26
+ ## Quick start
27
+
28
+ ```ts
29
+ import LinkTrail from 'linktrail-react-native';
30
+
31
+ // At app launch. The API key is required.
32
+ await LinkTrail.configure('lt_live_…');
33
+
34
+ // One listener handles both first-launch (deferred) AND re-engagement links:
35
+ LinkTrail.onLink((link, source) => {
36
+ router.navigate(link.path, link.customData); // e.g. "/products/aj1" + { voucher: "SUMMER25" }
37
+ });
38
+
39
+ // Observe failures:
40
+ LinkTrail.onError((error) => console.warn(`[LinkTrail] ${error.code}: ${error.message}`));
41
+ ```
42
+
43
+ The install is tracked automatically by `configure`, and incoming links (Universal Links / App
44
+ Links / custom schemes) are forwarded via React Native's `Linking` API — no extra wiring.
45
+
46
+ ## More
47
+
48
+ ```ts
49
+ // Custom post-install events:
50
+ await LinkTrail.trackEvent('purchase', { value: 59.99, currency: 'USD' });
51
+
52
+ // Cached results:
53
+ const attribution = await LinkTrail.getLastAttribution();
54
+ const lastLink = await LinkTrail.getLastDeepLink();
55
+
56
+ // Consent-gated install (defer configure's auto-track, then call manually):
57
+ await LinkTrail.configure('lt_live_…', { autoTrackInstall: false });
58
+ await LinkTrail.trackInstall();
59
+
60
+ // iOS ATT / SKAdNetwork (no-ops on Android):
61
+ await LinkTrail.requestTrackingAuthorization();
62
+ LinkTrail.registerForSKAdAttribution();
63
+ LinkTrail.updateConversionValue(42, 'medium');
64
+ ```
65
+
66
+ `configure` also takes `{ logEnabled, logLevel, requestTimeoutMillis, retryPolicy, linkDomains,
67
+ autoTrackInstall, autoHandleLinks }`. Set `autoHandleLinks: false` to forward URLs yourself via
68
+ `LinkTrail.handleDeepLink(url)`.
69
+
70
+ ## Deep-link setup
71
+
72
+ Standard app deep-link config — the wrapper handles the rest. Declare your LinkTrail host as a
73
+ Universal Link (iOS Associated Domains: `applinks:kick.linktrail.io`) and an App Links
74
+ `intent-filter` (Android), plus any custom scheme. LinkTrail infra hosts the
75
+ `apple-app-site-association` / `assetlinks.json` files for your link domains.
76
+
77
+ ## Example app
78
+
79
+ [`example/`](example/) is **KickFlip**, a storefront that shows deferred deep linking end to end,
80
+ consuming this package the same way your app would:
81
+
82
+ ```sh
83
+ cd example && npm install && npm run ios # or: npm run android
84
+ ```
85
+
86
+ Set your `lt_live_…` key in [`src/attribution.ts`](example/src/attribution.ts), then tap the 🔗
87
+ button to fire the four scenarios (home · category · product · product+voucher). The simulator
88
+ works without a key.
89
+
90
+ ## License
91
+
92
+ MIT
@@ -0,0 +1,74 @@
1
+ buildscript {
2
+ ext.getExtOrDefault = { name, fallback ->
3
+ rootProject.ext.has(name) ? rootProject.ext.get(name) : fallback
4
+ }
5
+
6
+ repositories {
7
+ google()
8
+ mavenCentral()
9
+ }
10
+
11
+ dependencies {
12
+ classpath "com.android.tools.build:gradle:8.6.0"
13
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion', '2.0.21')}"
14
+ }
15
+ }
16
+
17
+ def isNewArchitectureEnabled() {
18
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
19
+ }
20
+
21
+ if (!isNewArchitectureEnabled()) {
22
+ throw new GradleException(
23
+ "linktrail-react-native is a TurboModule and requires the React Native New Architecture. " +
24
+ "Enable it with newArchEnabled=true in gradle.properties (the default since React Native 0.76)."
25
+ )
26
+ }
27
+
28
+ apply plugin: "com.android.library"
29
+ apply plugin: "org.jetbrains.kotlin.android"
30
+ apply plugin: "com.facebook.react"
31
+
32
+ android {
33
+ namespace "io.linktrail.reactnative"
34
+
35
+ compileSdkVersion getExtOrDefault("compileSdkVersion", 35)
36
+
37
+ defaultConfig {
38
+ // The LinkTrail Android SDK requires API 26+.
39
+ minSdkVersion getExtOrDefault("minSdkVersion", 26)
40
+ targetSdkVersion getExtOrDefault("targetSdkVersion", 35)
41
+ }
42
+
43
+ compileOptions {
44
+ sourceCompatibility JavaVersion.VERSION_17
45
+ targetCompatibility JavaVersion.VERSION_17
46
+ }
47
+
48
+ kotlinOptions {
49
+ jvmTarget = "17"
50
+ }
51
+
52
+ sourceSets {
53
+ main {
54
+ java.srcDirs += ["generated/java", "generated/jni"]
55
+ }
56
+ }
57
+ }
58
+
59
+ react {
60
+ jsRootDir = file("../src/")
61
+ libraryName = "LinkTrailSpec"
62
+ codegenJavaPackageName = "io.linktrail.reactnative"
63
+ }
64
+
65
+ repositories {
66
+ google()
67
+ mavenCentral() // the LinkTrail SDK (io.linktrail:sdk) is published to Maven Central
68
+ }
69
+
70
+ dependencies {
71
+ implementation "com.facebook.react:react-android"
72
+ implementation "io.linktrail:sdk:0.0.3"
73
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
74
+ }
@@ -0,0 +1 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
@@ -0,0 +1,243 @@
1
+ package io.linktrail.reactnative
2
+
3
+ import android.net.Uri
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReadableMap
8
+ import com.facebook.react.bridge.ReadableType
9
+ import com.facebook.react.bridge.WritableMap
10
+ import io.linktrail.LinkTrail
11
+ import io.linktrail.LinkTrailError
12
+ import io.linktrail.LinkTrailLogLevel
13
+ import io.linktrail.LinkTrailOptions
14
+ import io.linktrail.LinkTrailRetryPolicy
15
+ import io.linktrail.model.LinkTrailAttribution
16
+ import io.linktrail.model.LinkTrailDeepLink
17
+ import io.linktrail.model.LinkTrailLinkSource
18
+ import kotlinx.coroutines.CoroutineScope
19
+ import kotlinx.coroutines.Dispatchers
20
+ import kotlinx.coroutines.SupervisorJob
21
+ import kotlinx.coroutines.cancel
22
+ import kotlinx.coroutines.launch
23
+
24
+ class LinkTrailModule(reactContext: ReactApplicationContext) :
25
+ NativeLinkTrailSpec(reactContext) {
26
+
27
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
28
+
29
+ override fun invalidate() {
30
+ scope.cancel()
31
+ super.invalidate()
32
+ }
33
+
34
+ // ── Configuration ──────────────────────────────────────────────────────────
35
+
36
+ override fun configure(apiKey: String, options: ReadableMap?, promise: Promise) {
37
+ try {
38
+ val sdk = LinkTrail.configure(reactApplicationContext, apiKey, parseOptions(options))
39
+ sdk.onAttribution { attribution -> emitOnAttribution(attributionToMap(attribution)) }
40
+ sdk.onLink { link, source ->
41
+ emitOnLink(
42
+ Arguments.createMap().apply {
43
+ putMap("link", deepLinkToMap(link))
44
+ putString("source", sourceName(source))
45
+ }
46
+ )
47
+ }
48
+ sdk.onError { error ->
49
+ emitOnError(
50
+ Arguments.createMap().apply {
51
+ putString("code", errorCode(error))
52
+ putString("message", error.message ?: "Unknown error")
53
+ }
54
+ )
55
+ }
56
+ promise.resolve(null)
57
+ } catch (t: Throwable) {
58
+ promise.reject(errorCode(t), t.message, t)
59
+ }
60
+ }
61
+
62
+ // ── Install / events ───────────────────────────────────────────────────────
63
+
64
+ override fun trackInstall(force: Boolean, promise: Promise) {
65
+ val sdk = requireSdk(promise) ?: return
66
+ scope.launch {
67
+ try {
68
+ promise.resolve(attributionToMap(sdk.trackInstallAsync(force)))
69
+ } catch (t: Throwable) {
70
+ promise.reject(errorCode(t), t.message, t)
71
+ }
72
+ }
73
+ }
74
+
75
+ override fun trackEvent(name: String, value: Double?, currency: String?, promise: Promise) {
76
+ val sdk = requireSdk(promise) ?: return
77
+ scope.launch {
78
+ try {
79
+ val result = sdk.trackEventAsync(name, value, currency)
80
+ promise.resolve(
81
+ Arguments.createMap().apply {
82
+ result.id?.let { putInt("id", it) }
83
+ putBoolean("attributed", result.attributed)
84
+ }
85
+ )
86
+ } catch (t: Throwable) {
87
+ promise.reject(errorCode(t), t.message, t)
88
+ }
89
+ }
90
+ }
91
+
92
+ // ── Deep linking ───────────────────────────────────────────────────────────
93
+
94
+ override fun handleDeepLink(url: String, promise: Promise) {
95
+ val sdk = requireSdk(promise) ?: return
96
+ scope.launch {
97
+ try {
98
+ promise.resolve(deepLinkToMap(sdk.handleDeepLinkAsync(Uri.parse(url))))
99
+ } catch (e: LinkTrailError.NotALinkTrailUrl) {
100
+ promise.resolve(null)
101
+ } catch (t: Throwable) {
102
+ promise.reject(errorCode(t), t.message, t)
103
+ }
104
+ }
105
+ }
106
+
107
+ override fun getLastAttribution(promise: Promise) {
108
+ promise.resolve(LinkTrail.shared?.lastAttribution?.let(::attributionToMap))
109
+ }
110
+
111
+ override fun getLastDeepLink(promise: Promise) {
112
+ promise.resolve(LinkTrail.shared?.lastDeepLink?.let(::deepLinkToMap))
113
+ }
114
+
115
+ // ── iOS-only APIs (no-ops on Android) ──────────────────────────────────────
116
+
117
+ override fun requestTrackingAuthorization(promise: Promise) {
118
+ promise.resolve(false)
119
+ }
120
+
121
+ override fun registerForSKAdAttribution() = Unit
122
+
123
+ override fun updateConversionValue(value: Double, coarseValue: String?) = Unit
124
+
125
+ // ── Testing ────────────────────────────────────────────────────────────────
126
+
127
+ override fun resetForTesting() {
128
+ LinkTrail.resetForTesting(reactApplicationContext)
129
+ }
130
+
131
+ // ── Helpers ────────────────────────────────────────────────────────────────
132
+
133
+ private fun requireSdk(promise: Promise): LinkTrail? {
134
+ val sdk = LinkTrail.shared
135
+ if (sdk == null) {
136
+ promise.reject(
137
+ "not_configured",
138
+ "LinkTrail is not configured. Call LinkTrail.configure(apiKey) first."
139
+ )
140
+ }
141
+ return sdk
142
+ }
143
+
144
+ private fun parseOptions(map: ReadableMap?): LinkTrailOptions {
145
+ if (map == null) return LinkTrailOptions()
146
+ val defaults = LinkTrailOptions()
147
+
148
+ val retryMap = if (map.hasKey("retryPolicy")) map.getMap("retryPolicy") else null
149
+ val defaultRetry = LinkTrailRetryPolicy.DEFAULT
150
+ val retryPolicy = if (retryMap == null) defaultRetry else LinkTrailRetryPolicy(
151
+ maxAttempts = retryMap.intOrDefault("maxAttempts", defaultRetry.maxAttempts),
152
+ baseDelayMillis = retryMap.longOrDefault("baseDelayMillis", defaultRetry.baseDelayMillis),
153
+ maxDelayMillis = retryMap.longOrDefault("maxDelayMillis", defaultRetry.maxDelayMillis),
154
+ )
155
+
156
+ val linkDomains = mutableListOf<String>()
157
+ if (map.hasKey("linkDomains")) {
158
+ map.getArray("linkDomains")?.let { array ->
159
+ for (i in 0 until array.size()) array.getString(i)?.let(linkDomains::add)
160
+ }
161
+ }
162
+
163
+ return LinkTrailOptions(
164
+ logEnabled = map.booleanOrDefault("logEnabled", defaults.logEnabled),
165
+ logLevel = logLevel(map.stringOrNull("logLevel"), defaults.logLevel),
166
+ requestTimeoutMillis = map.longOrDefault("requestTimeoutMillis", defaults.requestTimeoutMillis),
167
+ retryPolicy = retryPolicy,
168
+ linkDomains = linkDomains,
169
+ autoTrackInstall = map.booleanOrDefault("autoTrackInstall", defaults.autoTrackInstall),
170
+ )
171
+ }
172
+
173
+ private fun logLevel(name: String?, fallback: LinkTrailLogLevel): LinkTrailLogLevel =
174
+ when (name) {
175
+ "debug" -> LinkTrailLogLevel.DEBUG
176
+ "info" -> LinkTrailLogLevel.INFO
177
+ "warning" -> LinkTrailLogLevel.WARNING
178
+ "error" -> LinkTrailLogLevel.ERROR
179
+ "none" -> LinkTrailLogLevel.NONE
180
+ else -> fallback
181
+ }
182
+
183
+ private fun sourceName(source: LinkTrailLinkSource): String = when (source) {
184
+ LinkTrailLinkSource.DEFERRED -> "deferred"
185
+ LinkTrailLinkSource.REENGAGEMENT -> "reengagement"
186
+ }
187
+
188
+ private fun attributionToMap(attribution: LinkTrailAttribution): WritableMap =
189
+ Arguments.createMap().apply {
190
+ attribution.id?.let { putInt("id", it) }
191
+ putBoolean("attributed", attribution.attributed)
192
+ attribution.deepLink?.let { putMap("deepLink", deepLinkToMap(it)) }
193
+ }
194
+
195
+ private fun deepLinkToMap(link: LinkTrailDeepLink): WritableMap =
196
+ Arguments.createMap().apply {
197
+ link.slug?.let { putString("slug", it) }
198
+ link.url?.let { putString("url", it) }
199
+ link.deepLinkPath?.let { putString("deepLinkPath", it) }
200
+ link.iosUrl?.let { putString("iosUrl", it) }
201
+ link.androidUrl?.let { putString("androidUrl", it) }
202
+ link.fallbackUrl?.let { putString("fallbackUrl", it) }
203
+ link.campaign?.let { putString("campaign", it) }
204
+ link.channel?.let { putString("channel", it) }
205
+ link.utm?.let { putMap("utm", stringMapToMap(it)) }
206
+ link.customData?.let { putMap("customData", stringMapToMap(it)) }
207
+ putString("path", link.path)
208
+ putBoolean("hasRoutableDestination", link.hasRoutableDestination)
209
+ }
210
+
211
+ private fun stringMapToMap(values: Map<String, String>): WritableMap =
212
+ Arguments.createMap().apply {
213
+ values.forEach { (key, value) -> putString(key, value) }
214
+ }
215
+
216
+ private fun errorCode(error: Throwable): String = when (error) {
217
+ is LinkTrailError.MissingApiKey -> "missing_api_key"
218
+ is LinkTrailError.InvalidApiKey -> "invalid_api_key"
219
+ is LinkTrailError.Transport -> "transport"
220
+ is LinkTrailError.Server -> "server"
221
+ is LinkTrailError.Decoding -> "decoding"
222
+ is LinkTrailError.EmptyResponse -> "empty_response"
223
+ is LinkTrailError.InvalidUrl -> "invalid_url"
224
+ is LinkTrailError.NotALinkTrailUrl -> "not_a_linktrail_url"
225
+ else -> "unknown"
226
+ }
227
+
228
+ private fun ReadableMap.booleanOrDefault(key: String, fallback: Boolean): Boolean =
229
+ if (hasKey(key) && getType(key) == ReadableType.Boolean) getBoolean(key) else fallback
230
+
231
+ private fun ReadableMap.intOrDefault(key: String, fallback: Int): Int =
232
+ if (hasKey(key) && getType(key) == ReadableType.Number) getInt(key) else fallback
233
+
234
+ private fun ReadableMap.longOrDefault(key: String, fallback: Long): Long =
235
+ if (hasKey(key) && getType(key) == ReadableType.Number) getDouble(key).toLong() else fallback
236
+
237
+ private fun ReadableMap.stringOrNull(key: String): String? =
238
+ if (hasKey(key) && getType(key) == ReadableType.String) getString(key) else null
239
+
240
+ companion object {
241
+ const val NAME = "LinkTrail"
242
+ }
243
+ }
@@ -0,0 +1,26 @@
1
+ package io.linktrail.reactnative
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+
9
+ class LinkTrailPackage : BaseReactPackage() {
10
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
11
+ if (name == LinkTrailModule.NAME) LinkTrailModule(reactContext) else null
12
+
13
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider =
14
+ ReactModuleInfoProvider {
15
+ mapOf(
16
+ LinkTrailModule.NAME to ReactModuleInfo(
17
+ name = LinkTrailModule.NAME,
18
+ className = LinkTrailModule::class.java.name,
19
+ canOverrideExistingModule = false,
20
+ needsEagerInit = false,
21
+ isCxxModule = false,
22
+ isTurboModule = true,
23
+ )
24
+ )
25
+ }
26
+ }
@@ -0,0 +1,129 @@
1
+ // The codegen spec header (and the -Swift.h bridge) are C++-heavy; importing
2
+ // them only from this ObjC++ (.mm) translation unit — never from a public
3
+ // header — keeps them out of the pod's Clang module umbrella, which otherwise
4
+ // fails to build (libc++ headers unresolved under explicit modules).
5
+ #if __has_include(<LinkTrailSpec/LinkTrailSpec.h>)
6
+ #import <LinkTrailSpec/LinkTrailSpec.h>
7
+ #else
8
+ #import "LinkTrailSpec.h"
9
+ #endif
10
+
11
+ #if __has_include("LinktrailReactNative-Swift.h")
12
+ #import "LinktrailReactNative-Swift.h"
13
+ #else
14
+ #import <LinktrailReactNative/LinktrailReactNative-Swift.h>
15
+ #endif
16
+
17
+ // TurboModule registered as "LinkTrail". Delegates to LinkTrailModuleImpl
18
+ // (Swift), which talks to the binary LinkTrailSDK framework. Declared here
19
+ // (not in a public header) on purpose — see the note above.
20
+ @interface LinkTrailModule : NativeLinkTrailSpecBase <NativeLinkTrailSpec>
21
+ @end
22
+
23
+ @implementation LinkTrailModule {
24
+ LinkTrailModuleImpl *_impl;
25
+ }
26
+
27
+ RCT_EXPORT_MODULE(LinkTrail)
28
+
29
+ - (instancetype)init
30
+ {
31
+ if (self = [super init]) {
32
+ _impl = [LinkTrailModuleImpl new];
33
+ __weak LinkTrailModule *weakSelf = self;
34
+ _impl.onLink = ^(NSDictionary *payload) {
35
+ [weakSelf emitOnLink:payload];
36
+ };
37
+ _impl.onAttribution = ^(NSDictionary *payload) {
38
+ [weakSelf emitOnAttribution:payload];
39
+ };
40
+ _impl.onError = ^(NSDictionary *payload) {
41
+ [weakSelf emitOnError:payload];
42
+ };
43
+ }
44
+ return self;
45
+ }
46
+
47
+ - (void)configure:(NSString *)apiKey
48
+ options:(NSDictionary *)options
49
+ resolve:(RCTPromiseResolveBlock)resolve
50
+ reject:(RCTPromiseRejectBlock)reject
51
+ {
52
+ [_impl configure:apiKey
53
+ options:options
54
+ resolve:resolve
55
+ reject:^(NSString *code, NSString *message) { reject(code, message, nil); }];
56
+ }
57
+
58
+ - (void)trackInstall:(BOOL)force
59
+ resolve:(RCTPromiseResolveBlock)resolve
60
+ reject:(RCTPromiseRejectBlock)reject
61
+ {
62
+ [_impl trackInstall:force
63
+ resolve:resolve
64
+ reject:^(NSString *code, NSString *message) { reject(code, message, nil); }];
65
+ }
66
+
67
+ - (void)trackEvent:(NSString *)name
68
+ value:(NSNumber *)value
69
+ currency:(NSString *)currency
70
+ resolve:(RCTPromiseResolveBlock)resolve
71
+ reject:(RCTPromiseRejectBlock)reject
72
+ {
73
+ [_impl trackEvent:name
74
+ value:value
75
+ currency:currency
76
+ resolve:resolve
77
+ reject:^(NSString *code, NSString *message) { reject(code, message, nil); }];
78
+ }
79
+
80
+ - (void)handleDeepLink:(NSString *)url
81
+ resolve:(RCTPromiseResolveBlock)resolve
82
+ reject:(RCTPromiseRejectBlock)reject
83
+ {
84
+ [_impl handleDeepLink:url
85
+ resolve:resolve
86
+ reject:^(NSString *code, NSString *message) { reject(code, message, nil); }];
87
+ }
88
+
89
+ - (void)getLastAttribution:(RCTPromiseResolveBlock)resolve
90
+ reject:(RCTPromiseRejectBlock)reject
91
+ {
92
+ [_impl getLastAttribution:resolve];
93
+ }
94
+
95
+ - (void)getLastDeepLink:(RCTPromiseResolveBlock)resolve
96
+ reject:(RCTPromiseRejectBlock)reject
97
+ {
98
+ [_impl getLastDeepLink:resolve];
99
+ }
100
+
101
+ - (void)requestTrackingAuthorization:(RCTPromiseResolveBlock)resolve
102
+ reject:(RCTPromiseRejectBlock)reject
103
+ {
104
+ [_impl requestTrackingAuthorization:resolve
105
+ reject:^(NSString *code, NSString *message) { reject(code, message, nil); }];
106
+ }
107
+
108
+ - (void)registerForSKAdAttribution
109
+ {
110
+ [_impl registerForSKAdAttribution];
111
+ }
112
+
113
+ - (void)updateConversionValue:(double)value coarseValue:(NSString *)coarseValue
114
+ {
115
+ [_impl updateConversionValue:(NSInteger)llround(value) coarseValue:coarseValue];
116
+ }
117
+
118
+ - (void)resetForTesting
119
+ {
120
+ [_impl resetForTesting];
121
+ }
122
+
123
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
124
+ (const facebook::react::ObjCTurboModule::InitParams &)params
125
+ {
126
+ return std::make_shared<facebook::react::NativeLinkTrailSpecJSI>(params);
127
+ }
128
+
129
+ @end