@booleanmaths/booleanmaths-rn-sdk 0.1.1 → 0.3.0
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/BooleanmathsRnSdk.podspec +26 -0
- package/README.md +251 -91
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/booleanmathsrnsdk/BooleanmathsRnSdkModule.kt +11 -1
- package/ios/BMBooleanMathsBridge.swift +146 -0
- package/ios/BooleanmathsRnSdk.h +11 -10
- package/ios/BooleanmathsRnSdk.mm +73 -13
- package/lib/module/BooleanMaths.js +3 -0
- package/lib/module/BooleanMaths.js.map +1 -1
- package/lib/module/BooleanMaths.native.js +25 -12
- package/lib/module/BooleanMaths.native.js.map +1 -1
- package/lib/module/NativeBooleanmathsRnSdk.js.map +1 -1
- package/lib/typescript/src/BooleanMaths.d.ts.map +1 -1
- package/lib/typescript/src/BooleanMaths.native.d.ts.map +1 -1
- package/lib/typescript/src/NativeBooleanmathsRnSdk.d.ts +5 -0
- package/lib/typescript/src/NativeBooleanmathsRnSdk.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +29 -8
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/BooleanMaths.native.tsx +29 -12
- package/src/BooleanMaths.tsx +4 -0
- package/src/NativeBooleanmathsRnSdk.ts +5 -0
- package/src/types.ts +30 -8
|
@@ -10,11 +10,37 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package["license"]
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
|
+
# No platform override needed. BooleanMathsSDK 1.0.1 lowered its own floor to
|
|
14
|
+
# 15.1, which is exactly React Native's min_ios_version_supported — pinning a
|
|
15
|
+
# higher floor here would push it onto every consuming app.
|
|
13
16
|
s.platforms = { :ios => min_ios_version_supported }
|
|
14
17
|
s.source = { :git => "https://github.com/medront/react-native-booleanmaths-sdk.git", :tag => "v#{s.version}" }
|
|
15
18
|
|
|
19
|
+
# Globs `swift` so ios/BMBooleanMathsBridge.swift is compiled. That shim is
|
|
20
|
+
# mandatory, not stylistic: BooleanMaths is a pure Swift `@MainActor final
|
|
21
|
+
# class` with no Objective-C surface, so the .mm cannot reach it directly.
|
|
16
22
|
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
23
|
+
# Does not match the generated BooleanmathsRnSdk-Swift.h, which the .mm needs.
|
|
17
24
|
s.private_header_files = "ios/**/*.h"
|
|
18
25
|
|
|
26
|
+
s.swift_version = "5.9"
|
|
27
|
+
|
|
28
|
+
# `~> 1.0.1`, deliberately not `~> 1.0`: 1.0.0 is still published on Trunk
|
|
29
|
+
# carrying an iOS 17.0 deployment target, and resolving to it would break the
|
|
30
|
+
# install for any app below iOS 17.
|
|
31
|
+
s.dependency "BooleanMathsSDK", "~> 1.0.1"
|
|
32
|
+
|
|
33
|
+
# Mirrors Android's BuildConfig.WRAPPER_VERSION, which is read from
|
|
34
|
+
# package.json so the reported wrapper_version cannot drift from the published
|
|
35
|
+
# npm version — s.version is that same value. Consumed by the .mm as an
|
|
36
|
+
# Objective-C preprocessor define; it does not reach Swift.
|
|
37
|
+
#
|
|
38
|
+
# $(inherited) is load-bearing: without it this replaces the pod target's
|
|
39
|
+
# inherited defines instead of appending, dropping DEBUG, RCT_NEW_ARCH_ENABLED
|
|
40
|
+
# and the FOLLY_* flags that React Native's own xcconfig sets.
|
|
41
|
+
s.pod_target_xcconfig = {
|
|
42
|
+
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) BMRN_WRAPPER_VERSION=\\\"#{s.version}\\\""
|
|
43
|
+
}
|
|
44
|
+
|
|
19
45
|
install_modules_dependencies(s)
|
|
20
46
|
end
|
package/README.md
CHANGED
|
@@ -5,42 +5,90 @@ and attribution.
|
|
|
5
5
|
|
|
6
6
|
This package is a thin TurboModule bridge over the native
|
|
7
7
|
[`com.booleanmaths:bm-sdk`](https://central.sonatype.com/artifact/com.booleanmaths/bm-sdk)
|
|
8
|
-
Android SDK
|
|
8
|
+
Android SDK and the
|
|
9
|
+
[`BooleanMathsSDK`](https://cocoapods.org/pods/BooleanMathsSDK) iOS SDK.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
**One import, one API, both platforms.** There are no platform-specific entry
|
|
12
|
+
points — `import { BooleanMaths } from '@booleanmaths/booleanmaths-rn-sdk'` and
|
|
13
|
+
call the same methods everywhere.
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
---
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
> **Android only**. On iOS (and on web) this package installs and builds
|
|
16
|
-
> normally, but **every SDK call is a no-op and no events are tracked**.
|
|
17
|
+
## Platform support
|
|
17
18
|
|
|
18
19
|
| Platform | Native SDK | Behaviour |
|
|
19
20
|
| :------- | :--------- | :-------- |
|
|
20
|
-
| Android | ✅ `com.booleanmaths:bm-sdk:1.0.
|
|
21
|
-
| iOS |
|
|
21
|
+
| Android | ✅ `com.booleanmaths:bm-sdk:1.0.9` | Fully functional |
|
|
22
|
+
| iOS | ✅ `BooleanMathsSDK 1.0.1` | Event tracking fully functional — see below |
|
|
22
23
|
| Web | ❌ not published | Silent no-op, **never crashes** |
|
|
23
24
|
|
|
25
|
+
### What iOS supports
|
|
26
|
+
|
|
27
|
+
Event tracking is complete and at parity with Android. Attribution features
|
|
28
|
+
are not — they are deferred to a later release, so their absence is scope
|
|
29
|
+
rather than a bug.
|
|
30
|
+
|
|
31
|
+
| Feature | Android | iOS |
|
|
32
|
+
| :------ | :-----: | :-: |
|
|
33
|
+
| `initialize()` | ✅ | ✅ |
|
|
34
|
+
| `trackEvent()` with nested properties | ✅ | ✅ |
|
|
35
|
+
| `wrapper_type` / `wrapper_version` on every event | ✅ | ✅ |
|
|
36
|
+
| Automatic `app_opened` | ✅ | ✅ |
|
|
37
|
+
| Automatic `FirstOpen` (once per install) | ✅ | ✅ — but with no attribution payload, see below |
|
|
38
|
+
| Visitor ID and 30-minute session handling | ✅ | ✅ |
|
|
39
|
+
| Durable on-device queue surviving app restarts | ✅ | ✅ |
|
|
40
|
+
| Automatic flush when the app backgrounds | ✅ | ✅ |
|
|
41
|
+
| `getHelloMessage()` bridge smoke test | ✅ | ✅ |
|
|
42
|
+
| **Deep links / universal links** (`DeepLinkClick`) | ✅ | ❌ |
|
|
43
|
+
| **Push-notification attribution** (`NotificationClick`) | ✅ | ❌ |
|
|
44
|
+
| **`handleIntent()` / `handleNotificationIntent()`** | ✅ | ❌ no-op |
|
|
45
|
+
| **Install attribution** on `FirstOpen` | ✅ Play Install Referrer | ❌ `data` is `{}` |
|
|
46
|
+
|
|
47
|
+
Notes on the iOS gaps:
|
|
48
|
+
|
|
49
|
+
- **`handleIntent()` is safe to call on iOS.** It reaches a native no-op and
|
|
50
|
+
logs one dev-mode notice. Shared code does not need to branch on platform.
|
|
51
|
+
- **`FirstOpen` still fires on iOS**, once per install — it just carries no
|
|
52
|
+
campaign payload. `data` is `{}` (present but empty), so the wire shape will
|
|
53
|
+
not change when Apple Search Ads attribution lands.
|
|
54
|
+
- On iOS there is **no `DeepLinkClick` or `NotificationClick` event at all**,
|
|
55
|
+
and consequently no `data.attribution` block on subsequent events.
|
|
56
|
+
- **`flush()` is not exposed to JavaScript** on either platform. The iOS SDK
|
|
57
|
+
flushes automatically on `willResignActive` / `didBecomeActive`; it will be
|
|
58
|
+
exposed only once Android has an equivalent, so it can ship as a genuinely
|
|
59
|
+
cross-platform API.
|
|
60
|
+
- macOS and tvOS are out of scope. The native SDK compiles for them; this
|
|
61
|
+
wrapper targets iOS only.
|
|
62
|
+
|
|
24
63
|
### Why it does not crash
|
|
25
64
|
|
|
26
|
-
Two independent guards, so
|
|
65
|
+
Two independent guards, so an unsupported platform or an incomplete native
|
|
66
|
+
install can never take your app down:
|
|
27
67
|
|
|
28
68
|
1. **JavaScript gate.** `BooleanMaths` checks `Platform.OS` against an
|
|
29
|
-
allowlist (
|
|
69
|
+
allowlist (`['android', 'ios']`) and returns before touching the native
|
|
30
70
|
bridge. It also verifies the TurboModule actually resolved, which covers a
|
|
31
|
-
broken or incomplete native install
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
broken or incomplete native install — most often a JS install without a
|
|
72
|
+
native rebuild.
|
|
73
|
+
2. **Native backstop.** Every native call is wrapped so failures are logged
|
|
74
|
+
rather than thrown into JavaScript
|
|
75
|
+
([`safely()`](android/src/main/java/com/booleanmathsrnsdk/BooleanmathsRnSdkModule.kt)
|
|
76
|
+
on Android, `BMSafely()` in
|
|
77
|
+
[`ios/BooleanmathsRnSdk.mm`](ios/BooleanmathsRnSdk.mm)). Analytics should
|
|
78
|
+
never crash the host app.
|
|
79
|
+
|
|
80
|
+
On web the JavaScript gate is the whole story: a separate no-op implementation
|
|
81
|
+
([`src/BooleanMaths.tsx`](src/BooleanMaths.tsx)) is resolved by the bundler, so
|
|
82
|
+
shared code can call the SDK unconditionally without platform checks.
|
|
35
83
|
|
|
36
84
|
In development (`__DEV__`) you get **one** console warning per app launch, not
|
|
37
85
|
one per call.
|
|
38
86
|
|
|
39
|
-
###
|
|
87
|
+
### Optionally gate your own analytics code
|
|
40
88
|
|
|
41
89
|
Calling the SDK unconditionally is safe — you do not *need* to branch. But if
|
|
42
|
-
you want to avoid dead work, skip the dev warning, or show different UI,
|
|
43
|
-
on `isSupported`:
|
|
90
|
+
you want to avoid dead work on web, skip the dev warning, or show different UI,
|
|
91
|
+
branch on `isSupported`:
|
|
44
92
|
|
|
45
93
|
```ts
|
|
46
94
|
import { BooleanMaths } from '@booleanmaths/booleanmaths-rn-sdk';
|
|
@@ -48,21 +96,12 @@ import { BooleanMaths } from '@booleanmaths/booleanmaths-rn-sdk';
|
|
|
48
96
|
if (BooleanMaths.isSupported) {
|
|
49
97
|
BooleanMaths.initialize(API_KEY, PIXEL_ID);
|
|
50
98
|
} else {
|
|
51
|
-
//
|
|
99
|
+
// Web, or a native install that needs rebuilding. Fall back or do nothing.
|
|
52
100
|
}
|
|
53
101
|
```
|
|
54
102
|
|
|
55
|
-
`isSupported` is
|
|
56
|
-
often as you like.
|
|
57
|
-
|
|
58
|
-
### When the iOS SDK ships
|
|
59
|
-
|
|
60
|
-
Two changes, no API break for consumers:
|
|
61
|
-
|
|
62
|
-
1. Add the iOS dependency to [`BooleanmathsRnSdk.podspec`](BooleanmathsRnSdk.podspec)
|
|
63
|
-
and implement the method bodies in [`ios/BooleanmathsRnSdk.mm`](ios/BooleanmathsRnSdk.mm).
|
|
64
|
-
2. Add `'ios'` to `SUPPORTED_PLATFORMS` in
|
|
65
|
-
[`src/BooleanMaths.native.tsx`](src/BooleanMaths.native.tsx).
|
|
103
|
+
`isSupported` is `true` on Android and iOS. It is a plain boolean, evaluated at
|
|
104
|
+
module load — cheap to read as often as you like.
|
|
66
105
|
|
|
67
106
|
---
|
|
68
107
|
|
|
@@ -78,14 +117,17 @@ Then rebuild the native app — a Metro reload is **not** enough:
|
|
|
78
117
|
|
|
79
118
|
```sh
|
|
80
119
|
npx react-native run-android
|
|
120
|
+
# and/or
|
|
121
|
+
cd ios && pod install && cd .. && npx react-native run-ios
|
|
81
122
|
```
|
|
82
123
|
|
|
83
124
|
### Requirements
|
|
84
125
|
|
|
85
126
|
- React Native **0.80+** with the **New Architecture** enabled (this is a
|
|
86
127
|
TurboModule)
|
|
87
|
-
- Android
|
|
88
|
-
|
|
128
|
+
- **Android:** `minSdkVersion` 24 or higher (the native SDK requires it),
|
|
129
|
+
`compileSdkVersion 36`, Java 17
|
|
130
|
+
- **iOS:** deployment target **15.1** or higher, and **Xcode 16+**
|
|
89
131
|
|
|
90
132
|
### Android
|
|
91
133
|
|
|
@@ -98,20 +140,38 @@ To pin a different native SDK version, set this in your app's **root**
|
|
|
98
140
|
```gradle
|
|
99
141
|
buildscript {
|
|
100
142
|
ext {
|
|
101
|
-
BooleanmathsRnSdk_bmSdkVersion = "1.0.
|
|
143
|
+
BooleanmathsRnSdk_bmSdkVersion = "1.0.9"
|
|
102
144
|
}
|
|
103
145
|
}
|
|
104
146
|
```
|
|
105
147
|
|
|
106
148
|
### iOS
|
|
107
149
|
|
|
108
|
-
|
|
109
|
-
compiles and links, it just does not track anything.
|
|
150
|
+
Autolinking picks the module up; run `pod install` to pull in the native SDK.
|
|
110
151
|
|
|
111
152
|
```sh
|
|
112
153
|
cd ios && pod install
|
|
113
154
|
```
|
|
114
155
|
|
|
156
|
+
That resolves `BooleanMathsSDK` from CocoaPods Trunk. Points worth knowing:
|
|
157
|
+
|
|
158
|
+
- **Minimum deployment target is iOS 15.1**, matching React Native's own floor,
|
|
159
|
+
so adopting this SDK does not raise your app's minimum iOS version.
|
|
160
|
+
- **The pod is pinned to `~> 1.0.1`,** deliberately not `~> 1.0`. Version 1.0.0
|
|
161
|
+
is still published with an iOS 17.0 floor, and resolving to it would break
|
|
162
|
+
the install for apps below iOS 17. Confirm your `Podfile.lock` shows
|
|
163
|
+
`BooleanMathsSDK (1.0.1)` or newer.
|
|
164
|
+
- **The SDK ships as a closed-source, vendored *dynamic* XCFramework.**
|
|
165
|
+
CocoaPods embeds and re-signs it with your app's identity. If you use
|
|
166
|
+
`use_frameworks!`, both `:linkage => :static` and `:linkage => :dynamic`
|
|
167
|
+
are supported.
|
|
168
|
+
- **No privacy manifest work needed.** The SDK bundles its own
|
|
169
|
+
`PrivacyInfo.xcprivacy` inside the XCFramework, so App Store
|
|
170
|
+
privacy-manifest requirements are covered by the pod.
|
|
171
|
+
- **Xcode 16+ is required.** The SDK's `.swiftinterface` is emitted at Swift 6.
|
|
172
|
+
Library evolution is enabled, so it is not pinned to the exact Xcode that
|
|
173
|
+
built it — but the toolchain must understand Swift 6.
|
|
174
|
+
|
|
115
175
|
---
|
|
116
176
|
|
|
117
177
|
## Usage
|
|
@@ -146,34 +206,66 @@ ignored.
|
|
|
146
206
|
|
|
147
207
|
### `BooleanMaths.initialize(apiKey, pixelId): void`
|
|
148
208
|
|
|
149
|
-
Initializes the native SDK. Also registers this wrapper with the native SDK
|
|
150
|
-
|
|
151
|
-
|
|
209
|
+
Initializes the native SDK. Also registers this wrapper with the native SDK
|
|
210
|
+
**before** initializing, so that even the automatic `FirstOpen` and `app_opened`
|
|
211
|
+
events emitted during initialization already carry
|
|
212
|
+
`wrapper_type: "react-native"` and the wrapper version. That version is read
|
|
213
|
+
from `package.json` on both platforms, so it cannot drift from the published npm
|
|
214
|
+
version.
|
|
215
|
+
|
|
216
|
+
On Android it additionally forwards the launch intent (see
|
|
217
|
+
[Deep links](#deep-links-and-notification-attribution)); there is no iOS
|
|
218
|
+
equivalent.
|
|
152
219
|
|
|
153
220
|
### `BooleanMaths.trackEvent(name, properties?): void`
|
|
154
221
|
|
|
155
222
|
Records a custom event. `properties` defaults to `{}` and may contain strings,
|
|
156
223
|
numbers, booleans, nested objects, and arrays.
|
|
157
224
|
|
|
225
|
+
### `BooleanMaths.handleIntent(): void`
|
|
226
|
+
|
|
227
|
+
**Android only.** Forwards the current Activity's intent to the native SDK so
|
|
228
|
+
ad deep links, app links and push-notification campaign data are attributed.
|
|
229
|
+
Safe to call repeatedly — the native SDK de-duplicates intents it has already
|
|
230
|
+
seen.
|
|
231
|
+
|
|
232
|
+
This is the single entry point for every kind of launch intent; native SDK
|
|
233
|
+
1.0.9 unified them behind one method.
|
|
234
|
+
|
|
235
|
+
**A no-op on iOS**, and safe to call there — it logs one dev-mode notice and
|
|
236
|
+
returns. The reason is scope, not platform support: intents are an Android
|
|
237
|
+
concept, and iOS deep links / universal links are deferred to a later release.
|
|
238
|
+
You do not need to branch on platform before calling it.
|
|
239
|
+
|
|
158
240
|
### `BooleanMaths.handleNotificationIntent(): void`
|
|
159
241
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
242
|
+
Deprecated alias of `handleIntent()`, kept so existing callers keep working.
|
|
243
|
+
It calls straight through to the same native `handleIntent`, so there is no
|
|
244
|
+
behavioural difference — prefer `handleIntent()` in new code.
|
|
163
245
|
|
|
164
246
|
### `BooleanMaths.getHelloMessage(): string | null`
|
|
165
247
|
|
|
166
248
|
Bridge smoke test. Returns the native SDK's hello string, or `null` where there
|
|
167
|
-
is no native SDK.
|
|
249
|
+
is no native SDK. The string comes from the native SDK itself on both platforms
|
|
250
|
+
(not from this wrapper), so a correct value also proves the native artifact
|
|
251
|
+
actually linked — which on iOS is the quickest way to confirm the XCFramework
|
|
252
|
+
was embedded.
|
|
168
253
|
|
|
169
254
|
### `BooleanMaths.isSupported: boolean`
|
|
170
255
|
|
|
171
|
-
`true` only where a real native SDK is linked
|
|
256
|
+
`true` only where a real native SDK is linked — Android and iOS. `false` on
|
|
257
|
+
web, and on any platform where the native module failed to resolve (typically a
|
|
258
|
+
JS install without a native rebuild).
|
|
172
259
|
|
|
173
260
|
---
|
|
174
261
|
|
|
175
262
|
## Deep links and notification attribution
|
|
176
263
|
|
|
264
|
+
> **Android only.** This entire section does not apply to iOS, where
|
|
265
|
+
> `handleIntent()` is a no-op and no `DeepLinkClick` / `NotificationClick`
|
|
266
|
+
> events are produced. The code below is still safe to run unchanged on iOS —
|
|
267
|
+
> the calls simply do nothing.
|
|
268
|
+
|
|
177
269
|
The native SDK registers its `ActivityLifecycleCallbacks` **inside**
|
|
178
270
|
`initialize()`. In a React Native app that runs long after `MainActivity`'s
|
|
179
271
|
`onActivityCreated` has already fired, so **the intent that cold-started your
|
|
@@ -181,7 +273,7 @@ app is never seen by those callbacks.**
|
|
|
181
273
|
|
|
182
274
|
This wrapper works around it by forwarding `currentActivity.intent` at the end
|
|
183
275
|
of `initialize()`. For links that arrive while the app is already running, call
|
|
184
|
-
`
|
|
276
|
+
`handleIntent()` yourself:
|
|
185
277
|
|
|
186
278
|
```ts
|
|
187
279
|
useEffect(() => {
|
|
@@ -189,13 +281,13 @@ useEffect(() => {
|
|
|
189
281
|
|
|
190
282
|
// Deep link received while the app is running.
|
|
191
283
|
const link = Linking.addEventListener('url', () => {
|
|
192
|
-
BooleanMaths.
|
|
284
|
+
BooleanMaths.handleIntent();
|
|
193
285
|
});
|
|
194
286
|
|
|
195
287
|
// Notification tap that brought the app back to the foreground.
|
|
196
288
|
const state = AppState.addEventListener('change', (next) => {
|
|
197
289
|
if (next === 'active') {
|
|
198
|
-
BooleanMaths.
|
|
290
|
+
BooleanMaths.handleIntent();
|
|
199
291
|
}
|
|
200
292
|
});
|
|
201
293
|
|
|
@@ -255,9 +347,11 @@ Watch it land with:
|
|
|
255
347
|
adb logcat -s BooleanMathsSDK:D EventDispatcher:D
|
|
256
348
|
```
|
|
257
349
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
fields nested under `data.
|
|
350
|
+
The demo's tap intent is an `ACTION_VIEW` deep link, so a successful tap logs
|
|
351
|
+
`Deep link click tracked with campaign data.`, persists a `DeepLinkClick`
|
|
352
|
+
event, and dispatches it with the campaign fields nested under `data.link`. An
|
|
353
|
+
intent without `ACTION_VIEW` takes the `NotificationClick` path instead — see
|
|
354
|
+
[Automatic events](#automatic-events).
|
|
261
355
|
|
|
262
356
|
---
|
|
263
357
|
|
|
@@ -265,41 +359,85 @@ fields nested under `data.notification`.
|
|
|
265
359
|
|
|
266
360
|
The native SDK tracks these without any call from you:
|
|
267
361
|
|
|
268
|
-
| Event | When |
|
|
269
|
-
| :---- | :--- |
|
|
270
|
-
| `app_opened` | First Activity creation / SDK initialization |
|
|
271
|
-
| `FirstOpen` | Once per install
|
|
272
|
-
| `
|
|
362
|
+
| Event | When | Android | iOS |
|
|
363
|
+
| :---- | :--- | :-----: | :-: |
|
|
364
|
+
| `app_opened` | First Activity creation / SDK initialization | ✅ | ✅ |
|
|
365
|
+
| `FirstOpen` | Once per install | ✅ with Google Play Install Referrer attribution | ✅ but `data` is `{}` |
|
|
366
|
+
| `DeepLinkClick` | An `ACTION_VIEW` intent is handled — ad deep links and app links. Campaign fields nest under `data.link` | ✅ | ❌ |
|
|
367
|
+
| `NotificationClick` | Any other intent carrying campaign data is handled. Fields nest under `data.notification` | ✅ | ❌ |
|
|
273
368
|
|
|
274
|
-
|
|
275
|
-
|
|
369
|
+
On both platforms `FirstOpen` precedes `app_opened`, so a new install's stream
|
|
370
|
+
reads in order. `FirstOpen` is PascalCase on the wire on purpose — the two
|
|
371
|
+
platforms match byte-for-byte and backend install reporting keys off that exact
|
|
372
|
+
string.
|
|
276
373
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
374
|
+
Visitor ID and session ID (30-minute timeout) are generated and persisted
|
|
375
|
+
natively on both platforms.
|
|
376
|
+
|
|
377
|
+
**Attribution is Android-only.** Campaign data from a handled intent is
|
|
378
|
+
persisted by `AttributionManager` and attached to **every subsequent event** as
|
|
379
|
+
`data.attribution`. iOS produces no `data.attribution` block, since it handles
|
|
380
|
+
no intents; Apple Search Ads attribution is deferred to a later release.
|
|
381
|
+
|
|
382
|
+
### What counts as a campaign intent (native SDK v1.0.9)
|
|
383
|
+
|
|
384
|
+
`handleIntent` ignores an intent when **all** of these hold, which is what a
|
|
385
|
+
plain launcher tap looks like:
|
|
386
|
+
|
|
387
|
+
- `action` is `ACTION_MAIN`, **and**
|
|
388
|
+
- `categories` contains `CATEGORY_LAUNCHER`, **and**
|
|
389
|
+
- there are no extras, **and**
|
|
390
|
+
- `data` is `null`
|
|
391
|
+
|
|
392
|
+
Anything else with non-empty campaign data is attributed: `ACTION_VIEW` emits
|
|
393
|
+
`DeepLinkClick`, everything else emits `NotificationClick`. Intents are
|
|
394
|
+
de-duplicated in a `WeakHashMap`, so re-forwarding the same intent is a no-op.
|
|
395
|
+
|
|
396
|
+
> ℹ️ **This fixes a v1.0.7/v1.0.8 bug this README previously warned about.**
|
|
397
|
+
> Older native SDKs recorded `intent.action` unconditionally and emitted
|
|
398
|
+
> `NotificationClick` whenever the resulting map was non-empty — so an ordinary
|
|
399
|
+
> launcher open produced a spurious
|
|
400
|
+
> `{ "event": "NotificationClick", "data": { "notification": { "action": "android.intent.action.MAIN" } } }`
|
|
401
|
+
> and the event could not be used as a tap metric without downstream
|
|
402
|
+
> filtering. As of 1.0.9 the launcher case is skipped at the source, and real
|
|
403
|
+
> deep links are split out into their own `DeepLinkClick` event.
|
|
292
404
|
>
|
|
293
|
-
>
|
|
294
|
-
>
|
|
295
|
-
>
|
|
296
|
-
> (require deep-link data or extras before emitting the event), not here.
|
|
405
|
+
> If you built dashboards or alerts that filter `NotificationClick` down to
|
|
406
|
+
> real taps, revisit them: the noise is gone, and `ACTION_VIEW` taps now arrive
|
|
407
|
+
> under a **different event name**.
|
|
297
408
|
|
|
298
409
|
---
|
|
299
410
|
|
|
300
411
|
## How properties cross the bridge
|
|
301
412
|
|
|
302
|
-
|
|
413
|
+
Each platform normalizes `properties` at the native boundary before handing it
|
|
414
|
+
to the SDK. The rules differ because the two SDKs serialize differently — the
|
|
415
|
+
observable payload is the same in the cases that matter.
|
|
416
|
+
|
|
417
|
+
| Input | Android | iOS |
|
|
418
|
+
| :---- | :------ | :-- |
|
|
419
|
+
| `null` / `undefined` object value | Key dropped | Key dropped |
|
|
420
|
+
| `null` inside an array | Preserved (indices must not shift) | Preserved |
|
|
421
|
+
| Nested objects and arrays | Recursed | Recursed |
|
|
422
|
+
| Whole numbers (`3`) | Coerced to integer — see the Gson caveat below | No action needed; `JSONSerialization` already emits `3`, not `3.0` |
|
|
423
|
+
| Fractions (`999.5`) | Untouched | Untouched |
|
|
424
|
+
| Booleans | Untouched | Untouched |
|
|
425
|
+
| **`NaN` / `Infinity`** | Passed through as-is | **Key dropped** — see below |
|
|
426
|
+
|
|
427
|
+
### iOS: why `NaN` is dropped rather than passed through
|
|
428
|
+
|
|
429
|
+
This one is load-bearing, not cosmetic. The iOS SDK guards its writes with
|
|
430
|
+
`JSONSerialization.isValidJSONObject(...)` and **silently discards the payload**
|
|
431
|
+
when that returns false. A JS `NaN` or `Infinity` arrives as a non-finite
|
|
432
|
+
`NSNumber` and invalidates the whole object — so a single bad property value
|
|
433
|
+
could discard the event, or the entire outgoing batch. The wrapper strips those
|
|
434
|
+
keys at the boundary so the rest of the event survives.
|
|
435
|
+
|
|
436
|
+
`NSDate` and anything else not JSON-representable is dropped for the same
|
|
437
|
+
reason. Everything is handled recursively, so a `NaN` nested three objects deep
|
|
438
|
+
costs you that one key and nothing else.
|
|
439
|
+
|
|
440
|
+
### Android normalization
|
|
303
441
|
|
|
304
442
|
- **Integral numbers are converted to integers.** React Native passes every JS
|
|
305
443
|
number across the bridge as a `Double`, so `{ count: 3 }` would otherwise be
|
|
@@ -311,7 +449,7 @@ Two normalizations happen on the Android side, both worth knowing:
|
|
|
311
449
|
{ "value": 2499, "items": [{ "quantity": 2, "price": 999.5 }] }
|
|
312
450
|
```
|
|
313
451
|
|
|
314
|
-
> ⚠️ **Known native SDK limitation (v1.0.
|
|
452
|
+
> ⚠️ **Known native SDK limitation (still present in v1.0.9).** The integers do *not* survive
|
|
315
453
|
> to the wire. `EventDispatcher` re-reads the stored payload with
|
|
316
454
|
> `gson.fromJson(properties, Map::class.java)`, and Gson coerces every number
|
|
317
455
|
> in a raw `Map` to `Double` — so the request body ends up with `2499.0` and
|
|
@@ -325,28 +463,50 @@ Two normalizations happen on the Android side, both worth knowing:
|
|
|
325
463
|
serializes with Gson, which omits null map values anyway, so the emitted JSON
|
|
326
464
|
is identical. Nulls **inside arrays** are preserved so indices do not shift.
|
|
327
465
|
|
|
328
|
-
Failures in the native bridge are logged
|
|
329
|
-
|
|
466
|
+
Failures in the native bridge are logged rather than thrown into JavaScript —
|
|
467
|
+
analytics should never crash the host app. Android logs to
|
|
468
|
+
`adb logcat -s BooleanmathsRnSdk`; iOS logs through `RCTLogError`, visible in
|
|
469
|
+
Xcode's console and Metro.
|
|
330
470
|
|
|
331
471
|
---
|
|
332
472
|
|
|
333
473
|
## Troubleshooting
|
|
334
474
|
|
|
335
475
|
**No events arriving.** Confirm `BooleanMaths.isSupported` is `true`, then check
|
|
336
|
-
`adb logcat -s BooleanMathsSDK:D BooleanmathsRnSdk:D
|
|
337
|
-
|
|
476
|
+
the native logs — `adb logcat -s BooleanMathsSDK:D BooleanmathsRnSdk:D` on
|
|
477
|
+
Android, or Xcode's console on iOS. The native SDK logs each persisted event and
|
|
478
|
+
the full payload it sends.
|
|
338
479
|
|
|
339
480
|
**Warning: "native module could not be found".** The JS installed but the native
|
|
340
|
-
side did not. Rebuild the app (`npx react-native run-android
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
**
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
481
|
+
side did not. Rebuild the app (`npx react-native run-android`, or `pod install`
|
|
482
|
+
followed by `npx react-native run-ios`) rather than just reloading Metro.
|
|
483
|
+
|
|
484
|
+
**iOS: crash at launch with a dyld "Library not loaded" / "image not found"
|
|
485
|
+
error naming `BooleanMathsSDK`.** The XCFramework is a *dynamic* framework and
|
|
486
|
+
was not embedded. Confirm the `[CP] Embed Pods Frameworks` build phase exists on
|
|
487
|
+
your app target, then `pod deintegrate && pod install`. This is the one iOS
|
|
488
|
+
failure mode that builds cleanly and only shows up at runtime.
|
|
489
|
+
|
|
490
|
+
**iOS: `getHelloMessage()` returns an empty string.** The bridge resolved but the
|
|
491
|
+
native SDK call failed — check the Xcode console for a
|
|
492
|
+
`BooleanMaths getHelloMessage failed` error. An empty string here specifically
|
|
493
|
+
means the XCFramework did not link correctly.
|
|
494
|
+
|
|
495
|
+
**iOS: `pod install` fails to resolve `BooleanMathsSDK`.** Run
|
|
496
|
+
`pod repo update`, and confirm your app's deployment target is 15.1 or higher.
|
|
497
|
+
If the lockfile pinned `1.0.0`, delete that entry and reinstall — 1.0.0 carries
|
|
498
|
+
an iOS 17.0 floor.
|
|
499
|
+
|
|
500
|
+
**Events show a delay.** By design on both platforms. Each `trackEvent`
|
|
501
|
+
enqueues an immediate sync attempt; Android's WorkManager also runs a periodic
|
|
502
|
+
15-minute batch job and requires network connectivity, and iOS flushes when the
|
|
503
|
+
app backgrounds.
|
|
504
|
+
|
|
505
|
+
**Deep link not attributed.** Android only — see
|
|
506
|
+
[Deep links](#deep-links-and-notification-attribution); the launch intent needs
|
|
507
|
+
explicit forwarding, and `singleTask` activities need `setIntent`. On iOS deep
|
|
508
|
+
link attribution is not implemented at all
|
|
509
|
+
([what iOS supports](#what-ios-supports)).
|
|
350
510
|
|
|
351
511
|
---
|
|
352
512
|
|
|
@@ -358,7 +518,7 @@ explicit forwarding, and `singleTask` activities need `setIntent`.
|
|
|
358
518
|
|
|
359
519
|
## License
|
|
360
520
|
|
|
361
|
-
|
|
521
|
+
[Apache-2.0](LICENSE)
|
|
362
522
|
|
|
363
523
|
---
|
|
364
524
|
|
package/android/build.gradle
CHANGED
|
@@ -6,7 +6,7 @@ buildscript {
|
|
|
6
6
|
// Version of the native Android SDK (com.booleanmaths:bm-sdk) this wrapper
|
|
7
7
|
// is built against. Override with `ext.BooleanmathsRnSdk_bmSdkVersion` in
|
|
8
8
|
// your app's root build.gradle to pin a different one.
|
|
9
|
-
bmSdkVersion: "1.0.
|
|
9
|
+
bmSdkVersion: "1.0.9"
|
|
10
10
|
]
|
|
11
11
|
|
|
12
12
|
ext.getExtOrDefault = { prop ->
|
|
@@ -41,6 +41,16 @@ class BooleanmathsRnSdkModule(reactContext: ReactApplicationContext) :
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
override fun handleIntent() {
|
|
45
|
+
safely("handleIntent") { forwardCurrentIntent() }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Alias of [handleIntent]. The native SDK unified ad deep links, app links
|
|
50
|
+
* and push notifications behind `handleIntent` in 1.0.9 and kept
|
|
51
|
+
* `handleNotificationIntent` as a delegating alias; this mirrors that so
|
|
52
|
+
* apps written against the older name keep working unchanged.
|
|
53
|
+
*/
|
|
44
54
|
override fun handleNotificationIntent() {
|
|
45
55
|
safely("handleNotificationIntent") { forwardCurrentIntent() }
|
|
46
56
|
}
|
|
@@ -64,7 +74,7 @@ class BooleanmathsRnSdkModule(reactContext: ReactApplicationContext) :
|
|
|
64
74
|
|
|
65
75
|
// The native SDK de-duplicates intents it has already processed, so
|
|
66
76
|
// calling this more than once for the same intent is safe.
|
|
67
|
-
BooleanMathsSDK.
|
|
77
|
+
BooleanMathsSDK.handleIntent(intent)
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
/**
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import BooleanMathsSDK
|
|
3
|
+
|
|
4
|
+
/// Objective-C-visible shim over the pure Swift `BooleanMathsSDK`.
|
|
5
|
+
///
|
|
6
|
+
/// This file is mandatory, not a stylistic layer. `BooleanMaths` is declared
|
|
7
|
+
/// `@MainActor public final class` — it is not `@objc` and not `NSObject`-derived
|
|
8
|
+
/// (and cannot trivially become `@objc`, which requires `NSObject` inheritance).
|
|
9
|
+
/// The SDK's generated `BooleanMathsSDK-Swift.h` therefore contains zero
|
|
10
|
+
/// `@interface` declarations, so `#import <BooleanMathsSDK/...>` from
|
|
11
|
+
/// `BooleanmathsRnSdk.mm` resolves to an empty module and the TurboModule cannot
|
|
12
|
+
/// see the SDK at all.
|
|
13
|
+
///
|
|
14
|
+
/// Everything here takes only Objective-C-representable types and is reached
|
|
15
|
+
/// from the `.mm` through the pod's generated `BooleanmathsRnSdk-Swift.h`.
|
|
16
|
+
///
|
|
17
|
+
/// Behaviour is specified by the Android module
|
|
18
|
+
/// (`android/src/main/java/com/booleanmathsrnsdk/BooleanmathsRnSdkModule.kt`),
|
|
19
|
+
/// which is the reference implementation for this wrapper.
|
|
20
|
+
@objc(BMBooleanMathsBridge)
|
|
21
|
+
public final class BMBooleanMathsBridge: NSObject {
|
|
22
|
+
|
|
23
|
+
/// Matches Android's `BuildConfig.WRAPPER_TYPE`.
|
|
24
|
+
private static let wrapperType = "react-native"
|
|
25
|
+
|
|
26
|
+
// MARK: - Public API
|
|
27
|
+
|
|
28
|
+
/// Initializes the native SDK and stamps this wrapper's identity onto it.
|
|
29
|
+
///
|
|
30
|
+
/// `@MainActor` because `BooleanMaths.initialize` is main-actor isolated. The
|
|
31
|
+
/// caller in `BooleanmathsRnSdk.mm` hops with `dispatch_async` before invoking
|
|
32
|
+
/// this — never `dispatch_sync`, which deadlocks when the JS thread is the
|
|
33
|
+
/// main thread.
|
|
34
|
+
///
|
|
35
|
+
/// Named `initializeSdk` rather than `initialize` for the same reason the
|
|
36
|
+
/// codegen spec is: `+[NSObject initialize]` already exists.
|
|
37
|
+
@MainActor
|
|
38
|
+
@objc(initializeSdkWithApiKey:pixelId:wrapperVersion:)
|
|
39
|
+
public static func initializeSdk(
|
|
40
|
+
apiKey: String,
|
|
41
|
+
pixelId: String,
|
|
42
|
+
wrapperVersion: String
|
|
43
|
+
) {
|
|
44
|
+
// Before `initialize`, not after: `initialize` itself emits `FirstOpen` and
|
|
45
|
+
// `app_opened` internally, and those must already carry wrapper_type /
|
|
46
|
+
// wrapper_version. Same ordering rationale as Android.
|
|
47
|
+
//
|
|
48
|
+
// Do NOT port Android's second, post-initialize call. Android needs it
|
|
49
|
+
// because its native SDK can only persist to SharedPreferences once it holds
|
|
50
|
+
// an application context, which it acquires inside initialize(). iOS has no
|
|
51
|
+
// such gap: `WrapperConfigStore.set` writes to UserDefaults unconditionally,
|
|
52
|
+
// and `initialize` calls `rehydrate()`, which fills each field only where it
|
|
53
|
+
// is still nil — so a value set earlier in the process outranks the
|
|
54
|
+
// persisted one. One call, before initialize, is correct and sufficient.
|
|
55
|
+
BooleanMaths.shared.setWrapperConfig(type: wrapperType, version: wrapperVersion)
|
|
56
|
+
|
|
57
|
+
BooleanMaths.shared.initialize(apiKey: apiKey, pixelId: pixelId)
|
|
58
|
+
|
|
59
|
+
// There is no iOS equivalent of Android's forwardCurrentIntent(): deep links
|
|
60
|
+
// are deferred on iOS, so nothing is forwarded here. See `handleIntent` in
|
|
61
|
+
// BooleanmathsRnSdk.mm.
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Records a custom event. `properties` is sanitized first — see
|
|
65
|
+
/// `sanitized(value:allowNull:)` for why that is load-bearing.
|
|
66
|
+
///
|
|
67
|
+
/// `@MainActor` because `BooleanMaths.track` is main-actor isolated.
|
|
68
|
+
@MainActor
|
|
69
|
+
@objc(trackEventWithName:properties:)
|
|
70
|
+
public static func trackEvent(name: String, properties: [String: Any]?) {
|
|
71
|
+
BooleanMaths.shared.track(name, properties: sanitized(dictionary: properties ?? [:]))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Bridge smoke test. Deliberately not main-actor isolated: the TurboModule
|
|
75
|
+
/// method is synchronous and returns the value directly, so it must not hop
|
|
76
|
+
/// threads. `BooleanMaths.getHelloMessage()` is `nonisolated static` in the SDK
|
|
77
|
+
/// specifically to allow this.
|
|
78
|
+
///
|
|
79
|
+
/// A real SDK call rather than a wrapper-side literal, so it also proves the
|
|
80
|
+
/// XCFramework actually linked.
|
|
81
|
+
@objc(getHelloMessage)
|
|
82
|
+
public static func getHelloMessage() -> String {
|
|
83
|
+
BooleanMaths.getHelloMessage()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// MARK: - Properties sanitization
|
|
87
|
+
|
|
88
|
+
// `EventDispatcher` and `EventStore` both guard on
|
|
89
|
+
// `JSONSerialization.isValidJSONObject(...)` and silently drop the payload when
|
|
90
|
+
// it returns false. A JS `NaN` or `Infinity` arrives here as a non-finite
|
|
91
|
+
// NSNumber and makes the whole object invalid — so one bad property value can
|
|
92
|
+
// discard the event, or the entire outgoing batch. Android guards the same case
|
|
93
|
+
// in `normalizeValue`.
|
|
94
|
+
|
|
95
|
+
private static func sanitized(dictionary: [String: Any]) -> [String: Any] {
|
|
96
|
+
var result = [String: Any](minimumCapacity: dictionary.count)
|
|
97
|
+
|
|
98
|
+
for (key, value) in dictionary {
|
|
99
|
+
guard let clean = sanitized(value: value, allowNull: false) else { continue }
|
|
100
|
+
result[key] = clean
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Nulls are preserved inside arrays — dropping them would shift indices.
|
|
107
|
+
/// Android preserves them deliberately for the same reason.
|
|
108
|
+
private static func sanitized(array: [Any]) -> [Any] {
|
|
109
|
+
array.map { sanitized(value: $0, allowNull: true) ?? NSNull() }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private static func sanitized(value: Any, allowNull: Bool) -> Any? {
|
|
113
|
+
switch value {
|
|
114
|
+
case is NSNull:
|
|
115
|
+
// Dropped as an object value (matching Android, whose Gson omits null map
|
|
116
|
+
// values anyway), kept inside an array.
|
|
117
|
+
return allowNull ? NSNull() : nil
|
|
118
|
+
|
|
119
|
+
case let number as NSNumber:
|
|
120
|
+
// Must be tested before any `as Bool` cast: `NSNumber(1) as? Bool`
|
|
121
|
+
// succeeds under Swift bridging, so checking Bool first would silently
|
|
122
|
+
// turn `{ count: 1 }` into `{ count: true }`. Booleans arrive from React
|
|
123
|
+
// Native as CFBoolean, match here, and serialize as true/false unchanged.
|
|
124
|
+
//
|
|
125
|
+
// No whole-number coercion: Android's `toWholeNumberOrSelf` fixes a
|
|
126
|
+
// Gson-specific artifact, whereas JSONSerialization already emits `3`
|
|
127
|
+
// rather than `3.0`.
|
|
128
|
+
return number.doubleValue.isFinite ? number : nil
|
|
129
|
+
|
|
130
|
+
case let string as String:
|
|
131
|
+
return string
|
|
132
|
+
|
|
133
|
+
case let nested as [String: Any]:
|
|
134
|
+
return sanitized(dictionary: nested)
|
|
135
|
+
|
|
136
|
+
case let nested as [Any]:
|
|
137
|
+
return sanitized(array: nested)
|
|
138
|
+
|
|
139
|
+
default:
|
|
140
|
+
// NSDate and anything else not JSON-representable, plus dictionaries with
|
|
141
|
+
// non-String keys. React Native never produces these from a JS object, but
|
|
142
|
+
// passing one through would invalidate the payload.
|
|
143
|
+
return nil
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
package/ios/BooleanmathsRnSdk.h
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
#import <BooleanmathsRnSdkSpec/BooleanmathsRnSdkSpec.h>
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* iOS
|
|
4
|
+
* iOS implementation of the BooleanMaths TurboModule, backed by the
|
|
5
|
+
* `BooleanMathsSDK` pod (>= 1.0.1, minimum iOS 15.1).
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The SDK itself is pure Swift and exposes no Objective-C surface, so this class
|
|
8
|
+
* does not talk to it directly — every call goes through
|
|
9
|
+
* `BMBooleanMathsBridge` (`ios/BMBooleanMathsBridge.swift`), which explains why
|
|
10
|
+
* in detail.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
12
|
+
* `initializeSdk` and `trackEvent` hop to the main queue because the SDK is
|
|
13
|
+
* `@MainActor`; `getHelloMessage` deliberately does not, being synchronous and
|
|
14
|
+
* value-returning. Failures are logged and swallowed rather than thrown into JS.
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
16
|
+
* `handleIntent` and `handleNotificationIntent` remain no-ops: intents are an
|
|
17
|
+
* Android concept and iOS deep links are out of scope for this release.
|
|
17
18
|
*/
|
|
18
19
|
@interface BooleanmathsRnSdk : NSObject <NativeBooleanmathsRnSdkSpec>
|
|
19
20
|
|
package/ios/BooleanmathsRnSdk.mm
CHANGED
|
@@ -2,44 +2,104 @@
|
|
|
2
2
|
|
|
3
3
|
#import <React/RCTLog.h>
|
|
4
4
|
|
|
5
|
+
// The generated header for BMBooleanMathsBridge.swift. Both paths are needed so
|
|
6
|
+
// this builds under static and dynamic pod linkage alike.
|
|
7
|
+
#if __has_include(<BooleanmathsRnSdk/BooleanmathsRnSdk-Swift.h>)
|
|
8
|
+
#import <BooleanmathsRnSdk/BooleanmathsRnSdk-Swift.h>
|
|
9
|
+
#else
|
|
10
|
+
#import "BooleanmathsRnSdk-Swift.h"
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
// Injected by BooleanmathsRnSdk.podspec from package.json's version, mirroring
|
|
14
|
+
// Android's BuildConfig.WRAPPER_VERSION. Only reached if that xcconfig failed to
|
|
15
|
+
// apply, in which case a wrong-looking wrapper_version is far better than a
|
|
16
|
+
// build error in a consumer's app.
|
|
17
|
+
#ifndef BMRN_WRAPPER_VERSION
|
|
18
|
+
#define BMRN_WRAPPER_VERSION "unknown"
|
|
19
|
+
#endif
|
|
20
|
+
|
|
5
21
|
@implementation BooleanmathsRnSdk
|
|
6
22
|
|
|
7
23
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
24
|
+
* Analytics must never take the host app down. Mirrors Android's `safely()`
|
|
25
|
+
* helper: log the failure and swallow it rather than surfacing it to JS.
|
|
26
|
+
*/
|
|
27
|
+
static void BMSafely(NSString *operation, void (^block)(void))
|
|
28
|
+
{
|
|
29
|
+
@try {
|
|
30
|
+
block();
|
|
31
|
+
} @catch (NSException *exception) {
|
|
32
|
+
RCTLogError(@"[@booleanmaths/booleanmaths-rn-sdk] BooleanMaths %@ failed: %@", operation, exception);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Logs the "deep links are Android-only" notice at most once per app launch, so
|
|
38
|
+
* it is visible without flooding the console.
|
|
10
39
|
*/
|
|
11
|
-
static void
|
|
40
|
+
static void BMWarnIntentUnsupportedOnce(void)
|
|
12
41
|
{
|
|
13
42
|
static dispatch_once_t onceToken;
|
|
14
43
|
dispatch_once(&onceToken, ^{
|
|
15
|
-
RCTLogWarn(@"[@booleanmaths/booleanmaths-rn-sdk]
|
|
16
|
-
@"
|
|
17
|
-
@"
|
|
44
|
+
RCTLogWarn(@"[@booleanmaths/booleanmaths-rn-sdk] Deep-link and notification intent forwarding is "
|
|
45
|
+
@"Android-only; handleIntent() is a no-op on iOS. Event tracking is fully supported — "
|
|
46
|
+
@"only intent attribution is unavailable.");
|
|
18
47
|
});
|
|
19
48
|
}
|
|
20
49
|
|
|
21
50
|
- (void)initializeSdk:(NSString *)apiKey pixelId:(NSString *)pixelId
|
|
22
51
|
{
|
|
23
|
-
|
|
52
|
+
// Captured outside the block: it is a compile-time constant, not thread state.
|
|
53
|
+
NSString *wrapperVersion = @BMRN_WRAPPER_VERSION;
|
|
54
|
+
|
|
55
|
+
// BooleanMaths is @MainActor. dispatch_async, never dispatch_sync — the latter
|
|
56
|
+
// deadlocks whenever the JS thread is the main thread.
|
|
57
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
58
|
+
BMSafely(@"initialize", ^{
|
|
59
|
+
[BMBooleanMathsBridge initializeSdkWithApiKey:apiKey
|
|
60
|
+
pixelId:pixelId
|
|
61
|
+
wrapperVersion:wrapperVersion];
|
|
62
|
+
});
|
|
63
|
+
});
|
|
24
64
|
}
|
|
25
65
|
|
|
26
66
|
- (void)trackEvent:(NSString *)name properties:(NSDictionary *)properties
|
|
27
67
|
{
|
|
28
|
-
|
|
68
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
69
|
+
BMSafely(@"trackEvent", ^{
|
|
70
|
+
[BMBooleanMathsBridge trackEventWithName:name properties:properties];
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
- (void)handleIntent
|
|
76
|
+
{
|
|
77
|
+
// Deliberately still a no-op. Intents are an Android concept, and iOS deep
|
|
78
|
+
// links / universal links are deferred — their absence here is scope, not a
|
|
79
|
+
// missing implementation.
|
|
80
|
+
BMWarnIntentUnsupportedOnce();
|
|
29
81
|
}
|
|
30
82
|
|
|
31
83
|
- (void)handleNotificationIntent
|
|
32
84
|
{
|
|
33
|
-
//
|
|
34
|
-
|
|
85
|
+
// Alias of handleIntent, mirroring the Android bridge.
|
|
86
|
+
BMWarnIntentUnsupportedOnce();
|
|
35
87
|
}
|
|
36
88
|
|
|
37
89
|
- (NSString *)getHelloMessage
|
|
38
90
|
{
|
|
39
|
-
|
|
91
|
+
// No dispatch: this is synchronous and returns a value, so it must not hop
|
|
92
|
+
// threads. The SDK's getHelloMessage() is `nonisolated static` to allow it.
|
|
93
|
+
@try {
|
|
94
|
+
return [BMBooleanMathsBridge getHelloMessage];
|
|
95
|
+
} @catch (NSException *exception) {
|
|
96
|
+
RCTLogError(@"[@booleanmaths/booleanmaths-rn-sdk] BooleanMaths getHelloMessage failed: %@", exception);
|
|
40
97
|
|
|
41
|
-
|
|
42
|
-
|
|
98
|
+
// Empty rather than a plausible-looking message: this method exists to prove
|
|
99
|
+
// the native SDK linked, so a failure must read as a failure. The codegen
|
|
100
|
+
// spec declares a non-optional string, so it cannot be nil.
|
|
101
|
+
return @"";
|
|
102
|
+
}
|
|
43
103
|
}
|
|
44
104
|
|
|
45
105
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["hasWarned","warnOnce","__DEV__","console","warn","BooleanMaths","isSupported","initialize","_apiKey","_pixelId","trackEvent","_name","_properties","handleNotificationIntent","getHelloMessage"],"sourceRoot":"../../src","sources":["BooleanMaths.tsx"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAIA,SAAS,GAAG,KAAK;AAErB,SAASC,QAAQA,CAAA,EAAG;EAClB,IAAID,SAAS,IAAI,OAAOE,OAAO,KAAK,WAAW,IAAI,CAACA,OAAO,EAAE;IAC3D;EACF;EAEAF,SAAS,GAAG,IAAI;EAEhBG,OAAO,CAACC,IAAI,CACV,sFAAsF,GACpF,2EAA2E,GAC3E,8DACJ,CAAC;AACH;AAEA,OAAO,MAAMC,YAA6B,GAAG;EAC3CC,WAAW,EAAE,KAAK;EAElBC,UAAUA,CAACC,OAAe,EAAEC,QAAgB,EAAQ;IAClDR,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDS,UAAUA,CAACC,KAAa,EAAEC,WAAyC,EAAQ;IACzEX,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDY,wBAAwBA,CAAA,EAAS;IAC/
|
|
1
|
+
{"version":3,"names":["hasWarned","warnOnce","__DEV__","console","warn","BooleanMaths","isSupported","initialize","_apiKey","_pixelId","trackEvent","_name","_properties","handleIntent","handleNotificationIntent","getHelloMessage"],"sourceRoot":"../../src","sources":["BooleanMaths.tsx"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAIA,SAAS,GAAG,KAAK;AAErB,SAASC,QAAQA,CAAA,EAAG;EAClB,IAAID,SAAS,IAAI,OAAOE,OAAO,KAAK,WAAW,IAAI,CAACA,OAAO,EAAE;IAC3D;EACF;EAEAF,SAAS,GAAG,IAAI;EAEhBG,OAAO,CAACC,IAAI,CACV,sFAAsF,GACpF,2EAA2E,GAC3E,8DACJ,CAAC;AACH;AAEA,OAAO,MAAMC,YAA6B,GAAG;EAC3CC,WAAW,EAAE,KAAK;EAElBC,UAAUA,CAACC,OAAe,EAAEC,QAAgB,EAAQ;IAClDR,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDS,UAAUA,CAACC,KAAa,EAAEC,WAAyC,EAAQ;IACzEX,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDY,YAAYA,CAAA,EAAS;IACnBZ,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDa,wBAAwBA,CAAA,EAAS;IAC/Bb,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDc,eAAeA,CAAA,EAAkB;IAC/Bd,QAAQ,CAAC,CAAC;IACV,OAAO,IAAI;EACb;AACF,CAAC","ignoreList":[]}
|
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
import { Platform } from 'react-native';
|
|
4
4
|
import NativeBooleanmathsRnSdk from "./NativeBooleanmathsRnSdk.js";
|
|
5
5
|
/**
|
|
6
|
-
* The BooleanMaths native SDK
|
|
7
|
-
* iOS
|
|
8
|
-
*
|
|
6
|
+
* The BooleanMaths native SDK ships for Android (`com.booleanmaths:bm-sdk`) and
|
|
7
|
+
* iOS (`BooleanMathsSDK`). Any other platform — including out-of-tree React
|
|
8
|
+
* Native targets — has no native artifact, so every call below short-circuits
|
|
9
|
+
* and the SDK behaves as a silent no-op rather than crashing the app.
|
|
10
|
+
*
|
|
11
|
+
* Note that `handleIntent` is a no-op on iOS even though iOS is supported here:
|
|
12
|
+
* intents are an Android concept. See its JSDoc in `./types`.
|
|
9
13
|
*/
|
|
10
|
-
const SUPPORTED_PLATFORMS = ['android'];
|
|
14
|
+
const SUPPORTED_PLATFORMS = ['android', 'ios'];
|
|
11
15
|
const isPlatformSupported = SUPPORTED_PLATFORMS.includes(Platform.OS);
|
|
12
16
|
|
|
13
17
|
/**
|
|
@@ -23,11 +27,25 @@ function warnOnce() {
|
|
|
23
27
|
}
|
|
24
28
|
hasWarned = true;
|
|
25
29
|
if (!isPlatformSupported) {
|
|
26
|
-
console.warn(`[@booleanmaths/booleanmaths-rn-sdk] The BooleanMaths native SDK is not available on ${Platform.OS} ` + '(Android
|
|
30
|
+
console.warn(`[@booleanmaths/booleanmaths-rn-sdk] The BooleanMaths native SDK is not available on ${Platform.OS} ` + '(Android and iOS only). All SDK calls are no-ops on this platform and no ' + 'events will be tracked. Gate your calls on `BooleanMaths.isSupported` to ' + 'silence this warning — see the README\'s "Platform support" section.');
|
|
27
31
|
return;
|
|
28
32
|
}
|
|
29
33
|
console.warn('[@booleanmaths/booleanmaths-rn-sdk] The native module could not be found on ' + `${Platform.OS}. Rebuild the app after installing the package (a Metro ` + 'reload is not enough), and on iOS run `pod install`. All SDK calls are ' + 'no-ops until this is fixed.');
|
|
30
34
|
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Shared by `handleIntent` and its `handleNotificationIntent` alias. A
|
|
38
|
+
* standalone function rather than `this.handleIntent()` so the alias keeps
|
|
39
|
+
* working when it is pulled off the object, as in
|
|
40
|
+
* `const { handleNotificationIntent } = BooleanMaths`.
|
|
41
|
+
*/
|
|
42
|
+
function forwardCurrentIntent() {
|
|
43
|
+
if (!isSupported) {
|
|
44
|
+
warnOnce();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
NativeBooleanmathsRnSdk.handleIntent();
|
|
48
|
+
}
|
|
31
49
|
export const BooleanMaths = {
|
|
32
50
|
isSupported,
|
|
33
51
|
initialize(apiKey, pixelId) {
|
|
@@ -44,13 +62,8 @@ export const BooleanMaths = {
|
|
|
44
62
|
}
|
|
45
63
|
NativeBooleanmathsRnSdk.trackEvent(name, properties);
|
|
46
64
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
warnOnce();
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
NativeBooleanmathsRnSdk.handleNotificationIntent();
|
|
53
|
-
},
|
|
65
|
+
handleIntent: forwardCurrentIntent,
|
|
66
|
+
handleNotificationIntent: forwardCurrentIntent,
|
|
54
67
|
getHelloMessage() {
|
|
55
68
|
if (!isSupported) {
|
|
56
69
|
warnOnce();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Platform","NativeBooleanmathsRnSdk","SUPPORTED_PLATFORMS","isPlatformSupported","includes","OS","isSupported","hasWarned","warnOnce","__DEV__","console","warn","BooleanMaths","initialize","apiKey","pixelId","initializeSdk","trackEvent","name","properties","handleNotificationIntent","getHelloMessage"],"sourceRoot":"../../src","sources":["BooleanMaths.native.tsx"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,uBAAuB,MAAM,8BAA2B;AAG/D;AACA;AACA;AACA;AACA;AACA,MAAMC,mBAAsD,GAAG,
|
|
1
|
+
{"version":3,"names":["Platform","NativeBooleanmathsRnSdk","SUPPORTED_PLATFORMS","isPlatformSupported","includes","OS","isSupported","hasWarned","warnOnce","__DEV__","console","warn","forwardCurrentIntent","handleIntent","BooleanMaths","initialize","apiKey","pixelId","initializeSdk","trackEvent","name","properties","handleNotificationIntent","getHelloMessage"],"sourceRoot":"../../src","sources":["BooleanMaths.native.tsx"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,uBAAuB,MAAM,8BAA2B;AAG/D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,mBAAsD,GAAG,CAC7D,SAAS,EACT,KAAK,CACN;AAED,MAAMC,mBAAmB,GAAGD,mBAAmB,CAACE,QAAQ,CAACJ,QAAQ,CAACK,EAAE,CAAC;;AAErE;AACA;AACA;AACA;AACA;AACA,MAAMC,WAAW,GAAGH,mBAAmB,IAAIF,uBAAuB,IAAI,IAAI;AAE1E,IAAIM,SAAS,GAAG,KAAK;AAErB,SAASC,QAAQA,CAAA,EAAG;EAClB,IAAID,SAAS,IAAI,CAACE,OAAO,EAAE;IACzB;EACF;EAEAF,SAAS,GAAG,IAAI;EAEhB,IAAI,CAACJ,mBAAmB,EAAE;IACxBO,OAAO,CAACC,IAAI,CACV,uFAAuFX,QAAQ,CAACK,EAAE,GAAG,GACnG,2EAA2E,GAC3E,2EAA2E,GAC3E,sEACJ,CAAC;IACD;EACF;EAEAK,OAAO,CAACC,IAAI,CACV,8EAA8E,GAC5E,GAAGX,QAAQ,CAACK,EAAE,0DAA0D,GACxE,yEAAyE,GACzE,6BACJ,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,SAASO,oBAAoBA,CAAA,EAAS;EACpC,IAAI,CAACN,WAAW,EAAE;IAChBE,QAAQ,CAAC,CAAC;IACV;EACF;EAEAP,uBAAuB,CAAEY,YAAY,CAAC,CAAC;AACzC;AAEA,OAAO,MAAMC,YAA6B,GAAG;EAC3CR,WAAW;EAEXS,UAAUA,CAACC,MAAc,EAAEC,OAAe,EAAQ;IAChD,IAAI,CAACX,WAAW,EAAE;MAChBE,QAAQ,CAAC,CAAC;MACV;IACF;IAEAP,uBAAuB,CAAEiB,aAAa,CAACF,MAAM,EAAEC,OAAO,CAAC;EACzD,CAAC;EAEDE,UAAUA,CAACC,IAAY,EAAEC,UAAuC,GAAG,CAAC,CAAC,EAAQ;IAC3E,IAAI,CAACf,WAAW,EAAE;MAChBE,QAAQ,CAAC,CAAC;MACV;IACF;IAEAP,uBAAuB,CAAEkB,UAAU,CAACC,IAAI,EAAEC,UAAU,CAAC;EACvD,CAAC;EAEDR,YAAY,EAAED,oBAAoB;EAElCU,wBAAwB,EAAEV,oBAAoB;EAE9CW,eAAeA,CAAA,EAAkB;IAC/B,IAAI,CAACjB,WAAW,EAAE;MAChBE,QAAQ,CAAC,CAAC;MACV,OAAO,IAAI;IACb;IAEA,OAAOP,uBAAuB,CAAEsB,eAAe,CAAC,CAAC;EACnD;AACF,CAAC","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","get"],"sourceRoot":"../../src","sources":["NativeBooleanmathsRnSdk.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","get"],"sourceRoot":"../../src","sources":["NativeBooleanmathsRnSdk.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAsBpE;AACA;AACA;AACA;AACA,eAAeA,mBAAmB,CAACC,GAAG,CAAO,mBAAmB,CAAC","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BooleanMaths.d.ts","sourceRoot":"","sources":["../../../src/BooleanMaths.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA+B,MAAM,YAAS,CAAC;AAyB5E,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"BooleanMaths.d.ts","sourceRoot":"","sources":["../../../src/BooleanMaths.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA+B,MAAM,YAAS,CAAC;AAyB5E,eAAO,MAAM,YAAY,EAAE,eAuB1B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BooleanMaths.native.d.ts","sourceRoot":"","sources":["../../../src/BooleanMaths.native.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAA+B,MAAM,YAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"BooleanMaths.native.d.ts","sourceRoot":"","sources":["../../../src/BooleanMaths.native.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAA+B,MAAM,YAAS,CAAC;AAmE5E,eAAO,MAAM,YAAY,EAAE,eAiC1B,CAAC"}
|
|
@@ -9,6 +9,11 @@ export interface Spec extends TurboModule {
|
|
|
9
9
|
initializeSdk(apiKey: string, pixelId: string): void;
|
|
10
10
|
trackEvent(name: string, properties: Object): void;
|
|
11
11
|
/** Android only. No-op elsewhere. */
|
|
12
|
+
handleIntent(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Alias of `handleIntent`, kept so existing callers keep working. Android
|
|
15
|
+
* only. No-op elsewhere.
|
|
16
|
+
*/
|
|
12
17
|
handleNotificationIntent(): void;
|
|
13
18
|
/** Smoke test that the native bridge is wired up. */
|
|
14
19
|
getHelloMessage(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeBooleanmathsRnSdk.d.ts","sourceRoot":"","sources":["../../../src/NativeBooleanmathsRnSdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,qCAAqC;IACrC,wBAAwB,IAAI,IAAI,CAAC;IACjC,qDAAqD;IACrD,eAAe,IAAI,MAAM,CAAC;CAC3B;AAED;;;GAGG;;AACH,wBAAkE"}
|
|
1
|
+
{"version":3,"file":"NativeBooleanmathsRnSdk.d.ts","sourceRoot":"","sources":["../../../src/NativeBooleanmathsRnSdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,qCAAqC;IACrC,YAAY,IAAI,IAAI,CAAC;IACrB;;;OAGG;IACH,wBAAwB,IAAI,IAAI,CAAC;IACjC,qDAAqD;IACrD,eAAe,IAAI,MAAM,CAAC;CAC3B;AAED;;;GAGG;;AACH,wBAAkE"}
|
|
@@ -25,22 +25,43 @@ export interface BooleanMathsApi {
|
|
|
25
25
|
*/
|
|
26
26
|
trackEvent(name: string, properties?: BooleanMathsEventProperties): void;
|
|
27
27
|
/**
|
|
28
|
-
* Forwards the current Activity's intent to the native SDK so deep
|
|
29
|
-
* notification campaign data
|
|
28
|
+
* Forwards the current Activity's intent to the native SDK so ad deep links,
|
|
29
|
+
* app links and push-notification campaign data are attributed.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
* deep-link handler
|
|
33
|
-
*
|
|
31
|
+
* This is the primary entry point for every kind of launch intent. Call it
|
|
32
|
+
* from your deep-link handler; `initialize` already forwards the launch
|
|
33
|
+
* intent itself (see the README for why that needs special handling).
|
|
34
|
+
*
|
|
35
|
+
* **Android only.** A no-op on iOS — not because iOS is unsupported (event
|
|
36
|
+
* tracking works fully there), but because intents are an Android concept and
|
|
37
|
+
* iOS deep links / universal links are out of scope for now. Safe to call
|
|
38
|
+
* unconditionally from shared code; on iOS it logs one dev-mode notice.
|
|
39
|
+
*
|
|
40
|
+
* Safe to call repeatedly — the native SDK de-duplicates intents it has
|
|
41
|
+
* already processed.
|
|
42
|
+
*/
|
|
43
|
+
handleIntent(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Backward-compatible alias of {@link handleIntent}.
|
|
46
|
+
*
|
|
47
|
+
* @deprecated Prefer `handleIntent()`, which names what it actually does:
|
|
48
|
+
* the native SDK routes deep links, app links and notifications through the
|
|
49
|
+
* same path. This alias will be kept for the foreseeable future.
|
|
34
50
|
*/
|
|
35
51
|
handleNotificationIntent(): void;
|
|
36
52
|
/**
|
|
37
53
|
* Returns the native SDK's hello message, or `null` when there is no native
|
|
38
|
-
* SDK on this platform.
|
|
54
|
+
* SDK on this platform.
|
|
55
|
+
*
|
|
56
|
+
* Useful as a bridge smoke test: the string comes from the native SDK itself
|
|
57
|
+
* on both Android and iOS, so a correct value also proves the native artifact
|
|
58
|
+
* actually linked.
|
|
39
59
|
*/
|
|
40
60
|
getHelloMessage(): string | null;
|
|
41
61
|
/**
|
|
42
|
-
* `true` only on platforms where a real BooleanMaths native SDK is linked
|
|
43
|
-
*
|
|
62
|
+
* `true` only on platforms where a real BooleanMaths native SDK is linked —
|
|
63
|
+
* Android and iOS. `false` on web, and on any platform where the native
|
|
64
|
+
* module failed to resolve (typically a JS install without a native rebuild).
|
|
44
65
|
*/
|
|
45
66
|
readonly isSupported: boolean;
|
|
46
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GACjC,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,yBAAyB,EAAE,GAC3B;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,CAAA;CAAE,CAAC;AAEjD,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAC9C,MAAM,EACN,yBAAyB,CAC1B,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAElD;;;;;OAKG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAEzE
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GACjC,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,yBAAyB,EAAE,GAC3B;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,yBAAyB,CAAA;CAAE,CAAC;AAEjD,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAC9C,MAAM,EACN,yBAAyB,CAC1B,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAElD;;;;;OAKG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAEzE;;;;;;;;;;;;;;;OAeG;IACH,YAAY,IAAI,IAAI,CAAC;IAErB;;;;;;OAMG;IACH,wBAAwB,IAAI,IAAI,CAAC;IAEjC;;;;;;;OAOG;IACH,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IAEjC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@booleanmaths/booleanmaths-rn-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "React Native SDK for BooleanMaths — track user events and attribution in mobile apps.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -3,11 +3,18 @@ import NativeBooleanmathsRnSdk from './NativeBooleanmathsRnSdk';
|
|
|
3
3
|
import type { BooleanMathsApi, BooleanMathsEventProperties } from './types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* The BooleanMaths native SDK
|
|
7
|
-
* iOS
|
|
8
|
-
*
|
|
6
|
+
* The BooleanMaths native SDK ships for Android (`com.booleanmaths:bm-sdk`) and
|
|
7
|
+
* iOS (`BooleanMathsSDK`). Any other platform — including out-of-tree React
|
|
8
|
+
* Native targets — has no native artifact, so every call below short-circuits
|
|
9
|
+
* and the SDK behaves as a silent no-op rather than crashing the app.
|
|
10
|
+
*
|
|
11
|
+
* Note that `handleIntent` is a no-op on iOS even though iOS is supported here:
|
|
12
|
+
* intents are an Android concept. See its JSDoc in `./types`.
|
|
9
13
|
*/
|
|
10
|
-
const SUPPORTED_PLATFORMS: ReadonlyArray<typeof Platform.OS> = [
|
|
14
|
+
const SUPPORTED_PLATFORMS: ReadonlyArray<typeof Platform.OS> = [
|
|
15
|
+
'android',
|
|
16
|
+
'ios',
|
|
17
|
+
];
|
|
11
18
|
|
|
12
19
|
const isPlatformSupported = SUPPORTED_PLATFORMS.includes(Platform.OS);
|
|
13
20
|
|
|
@@ -30,7 +37,7 @@ function warnOnce() {
|
|
|
30
37
|
if (!isPlatformSupported) {
|
|
31
38
|
console.warn(
|
|
32
39
|
`[@booleanmaths/booleanmaths-rn-sdk] The BooleanMaths native SDK is not available on ${Platform.OS} ` +
|
|
33
|
-
'(Android
|
|
40
|
+
'(Android and iOS only). All SDK calls are no-ops on this platform and no ' +
|
|
34
41
|
'events will be tracked. Gate your calls on `BooleanMaths.isSupported` to ' +
|
|
35
42
|
'silence this warning — see the README\'s "Platform support" section.'
|
|
36
43
|
);
|
|
@@ -45,6 +52,21 @@ function warnOnce() {
|
|
|
45
52
|
);
|
|
46
53
|
}
|
|
47
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Shared by `handleIntent` and its `handleNotificationIntent` alias. A
|
|
57
|
+
* standalone function rather than `this.handleIntent()` so the alias keeps
|
|
58
|
+
* working when it is pulled off the object, as in
|
|
59
|
+
* `const { handleNotificationIntent } = BooleanMaths`.
|
|
60
|
+
*/
|
|
61
|
+
function forwardCurrentIntent(): void {
|
|
62
|
+
if (!isSupported) {
|
|
63
|
+
warnOnce();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
NativeBooleanmathsRnSdk!.handleIntent();
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
export const BooleanMaths: BooleanMathsApi = {
|
|
49
71
|
isSupported,
|
|
50
72
|
|
|
@@ -66,14 +88,9 @@ export const BooleanMaths: BooleanMathsApi = {
|
|
|
66
88
|
NativeBooleanmathsRnSdk!.trackEvent(name, properties);
|
|
67
89
|
},
|
|
68
90
|
|
|
69
|
-
|
|
70
|
-
if (!isSupported) {
|
|
71
|
-
warnOnce();
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
91
|
+
handleIntent: forwardCurrentIntent,
|
|
74
92
|
|
|
75
|
-
|
|
76
|
-
},
|
|
93
|
+
handleNotificationIntent: forwardCurrentIntent,
|
|
77
94
|
|
|
78
95
|
getHelloMessage(): string | null {
|
|
79
96
|
if (!isSupported) {
|
package/src/BooleanMaths.tsx
CHANGED
|
@@ -10,6 +10,11 @@ export interface Spec extends TurboModule {
|
|
|
10
10
|
initializeSdk(apiKey: string, pixelId: string): void;
|
|
11
11
|
trackEvent(name: string, properties: Object): void;
|
|
12
12
|
/** Android only. No-op elsewhere. */
|
|
13
|
+
handleIntent(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Alias of `handleIntent`, kept so existing callers keep working. Android
|
|
16
|
+
* only. No-op elsewhere.
|
|
17
|
+
*/
|
|
13
18
|
handleNotificationIntent(): void;
|
|
14
19
|
/** Smoke test that the native bridge is wired up. */
|
|
15
20
|
getHelloMessage(): string;
|
package/src/types.ts
CHANGED
|
@@ -37,24 +37,46 @@ export interface BooleanMathsApi {
|
|
|
37
37
|
trackEvent(name: string, properties?: BooleanMathsEventProperties): void;
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Forwards the current Activity's intent to the native SDK so deep
|
|
41
|
-
* notification campaign data
|
|
40
|
+
* Forwards the current Activity's intent to the native SDK so ad deep links,
|
|
41
|
+
* app links and push-notification campaign data are attributed.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
44
|
-
* deep-link handler
|
|
45
|
-
*
|
|
43
|
+
* This is the primary entry point for every kind of launch intent. Call it
|
|
44
|
+
* from your deep-link handler; `initialize` already forwards the launch
|
|
45
|
+
* intent itself (see the README for why that needs special handling).
|
|
46
|
+
*
|
|
47
|
+
* **Android only.** A no-op on iOS — not because iOS is unsupported (event
|
|
48
|
+
* tracking works fully there), but because intents are an Android concept and
|
|
49
|
+
* iOS deep links / universal links are out of scope for now. Safe to call
|
|
50
|
+
* unconditionally from shared code; on iOS it logs one dev-mode notice.
|
|
51
|
+
*
|
|
52
|
+
* Safe to call repeatedly — the native SDK de-duplicates intents it has
|
|
53
|
+
* already processed.
|
|
54
|
+
*/
|
|
55
|
+
handleIntent(): void;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Backward-compatible alias of {@link handleIntent}.
|
|
59
|
+
*
|
|
60
|
+
* @deprecated Prefer `handleIntent()`, which names what it actually does:
|
|
61
|
+
* the native SDK routes deep links, app links and notifications through the
|
|
62
|
+
* same path. This alias will be kept for the foreseeable future.
|
|
46
63
|
*/
|
|
47
64
|
handleNotificationIntent(): void;
|
|
48
65
|
|
|
49
66
|
/**
|
|
50
67
|
* Returns the native SDK's hello message, or `null` when there is no native
|
|
51
|
-
* SDK on this platform.
|
|
68
|
+
* SDK on this platform.
|
|
69
|
+
*
|
|
70
|
+
* Useful as a bridge smoke test: the string comes from the native SDK itself
|
|
71
|
+
* on both Android and iOS, so a correct value also proves the native artifact
|
|
72
|
+
* actually linked.
|
|
52
73
|
*/
|
|
53
74
|
getHelloMessage(): string | null;
|
|
54
75
|
|
|
55
76
|
/**
|
|
56
|
-
* `true` only on platforms where a real BooleanMaths native SDK is linked
|
|
57
|
-
*
|
|
77
|
+
* `true` only on platforms where a real BooleanMaths native SDK is linked —
|
|
78
|
+
* Android and iOS. `false` on web, and on any platform where the native
|
|
79
|
+
* module failed to resolve (typically a JS install without a native rebuild).
|
|
58
80
|
*/
|
|
59
81
|
readonly isSupported: boolean;
|
|
60
82
|
}
|