@booleanmaths/booleanmaths-rn-sdk 0.2.0 → 0.4.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 +28 -0
- package/README.md +253 -70
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/booleanmathsrnsdk/BooleanmathsRnSdkModule.kt +7 -2
- package/ios/BMBooleanMathsBridge.swift +151 -0
- package/ios/BooleanmathsRnSdk.h +11 -10
- package/ios/BooleanmathsRnSdk.mm +70 -15
- package/lib/module/BooleanMaths.js +1 -1
- package/lib/module/BooleanMaths.js.map +1 -1
- package/lib/module/BooleanMaths.native.js +15 -7
- package/lib/module/BooleanMaths.native.js.map +1 -1
- package/lib/module/NativeBooleanmathsRnSdk.js.map +1 -1
- package/lib/typescript/src/BooleanMaths.native.d.ts.map +1 -1
- package/lib/typescript/src/NativeBooleanmathsRnSdk.d.ts +6 -1
- package/lib/typescript/src/NativeBooleanmathsRnSdk.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +30 -8
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/BooleanMaths.native.tsx +17 -7
- package/src/BooleanMaths.tsx +1 -1
- package/src/NativeBooleanmathsRnSdk.ts +6 -1
- package/src/types.ts +30 -8
|
@@ -10,11 +10,39 @@ 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 has declared a 15.1 floor since
|
|
14
|
+
# 1.0.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.1` resolves to >= 1.1, < 2.0. The earlier pin carried a `.1` patch
|
|
29
|
+
# component purely to exclude 1.0.0, which is still published on Trunk carrying
|
|
30
|
+
# an iOS 17.0 deployment target and would break the install for any app below
|
|
31
|
+
# iOS 17. A 1.1 floor excludes it outright, so the patch component is no longer
|
|
32
|
+
# load-bearing and the range can stay open across minors.
|
|
33
|
+
s.dependency "BooleanMathsSDK", "~> 1.1"
|
|
34
|
+
|
|
35
|
+
# Mirrors Android's BuildConfig.WRAPPER_VERSION, which is read from
|
|
36
|
+
# package.json so the reported wrapper_version cannot drift from the published
|
|
37
|
+
# npm version — s.version is that same value. Consumed by the .mm as an
|
|
38
|
+
# Objective-C preprocessor define; it does not reach Swift.
|
|
39
|
+
#
|
|
40
|
+
# $(inherited) is load-bearing: without it this replaces the pod target's
|
|
41
|
+
# inherited defines instead of appending, dropping DEBUG, RCT_NEW_ARCH_ENABLED
|
|
42
|
+
# and the FOLLY_* flags that React Native's own xcconfig sets.
|
|
43
|
+
s.pod_target_xcconfig = {
|
|
44
|
+
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) BMRN_WRAPPER_VERSION=\\\"#{s.version}\\\""
|
|
45
|
+
}
|
|
46
|
+
|
|
19
47
|
install_modules_dependencies(s)
|
|
20
48
|
end
|
package/README.md
CHANGED
|
@@ -5,42 +5,92 @@ 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.12` | Fully functional |
|
|
22
|
+
| iOS | ✅ `BooleanMathsSDK 1.1.0` | 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
|
+
| `isDebug` → `environment` on every event | ✅ | ✅ |
|
|
37
|
+
| `app_info` (package, version, install/update times) on every event | ✅ | ✅ |
|
|
38
|
+
| Automatic `app_opened` | ✅ | ✅ |
|
|
39
|
+
| Automatic `FirstOpen` (once per install) | ✅ | ✅ — but with no attribution payload, see below |
|
|
40
|
+
| Visitor ID and 30-minute session handling | ✅ | ✅ |
|
|
41
|
+
| Durable on-device queue surviving app restarts | ✅ | ✅ |
|
|
42
|
+
| Automatic flush when the app backgrounds | ✅ | ✅ |
|
|
43
|
+
| `getHelloMessage()` bridge smoke test | ✅ | ✅ |
|
|
44
|
+
| **Deep links / universal links** (`DeepLinkClick`) | ✅ | ❌ |
|
|
45
|
+
| **Push-notification attribution** (`NotificationClick`) | ✅ | ❌ |
|
|
46
|
+
| **`handleIntent()` / `handleNotificationIntent()`** | ✅ | ❌ no-op |
|
|
47
|
+
| **Install attribution** on `FirstOpen` | ✅ Play Install Referrer | ❌ `data` is `{}` |
|
|
48
|
+
|
|
49
|
+
Notes on the iOS gaps:
|
|
50
|
+
|
|
51
|
+
- **`handleIntent()` is safe to call on iOS.** It reaches a native no-op and
|
|
52
|
+
logs one dev-mode notice. Shared code does not need to branch on platform.
|
|
53
|
+
- **`FirstOpen` still fires on iOS**, once per install — it just carries no
|
|
54
|
+
campaign payload. `data` is `{}` (present but empty), so the wire shape will
|
|
55
|
+
not change when Apple Search Ads attribution lands.
|
|
56
|
+
- On iOS there is **no `DeepLinkClick` or `NotificationClick` event at all**,
|
|
57
|
+
and consequently no `data.attribution` block on subsequent events.
|
|
58
|
+
- **`flush()` is not exposed to JavaScript** on either platform. The iOS SDK
|
|
59
|
+
flushes automatically on `willResignActive` / `didBecomeActive`; it will be
|
|
60
|
+
exposed only once Android has an equivalent, so it can ship as a genuinely
|
|
61
|
+
cross-platform API.
|
|
62
|
+
- macOS and tvOS are out of scope. The native SDK compiles for them; this
|
|
63
|
+
wrapper targets iOS only.
|
|
64
|
+
|
|
24
65
|
### Why it does not crash
|
|
25
66
|
|
|
26
|
-
Two independent guards, so
|
|
67
|
+
Two independent guards, so an unsupported platform or an incomplete native
|
|
68
|
+
install can never take your app down:
|
|
27
69
|
|
|
28
70
|
1. **JavaScript gate.** `BooleanMaths` checks `Platform.OS` against an
|
|
29
|
-
allowlist (
|
|
71
|
+
allowlist (`['android', 'ios']`) and returns before touching the native
|
|
30
72
|
bridge. It also verifies the TurboModule actually resolved, which covers a
|
|
31
|
-
broken or incomplete native install
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
73
|
+
broken or incomplete native install — most often a JS install without a
|
|
74
|
+
native rebuild.
|
|
75
|
+
2. **Native backstop.** Every native call is wrapped so failures are logged
|
|
76
|
+
rather than thrown into JavaScript
|
|
77
|
+
([`safely()`](android/src/main/java/com/booleanmathsrnsdk/BooleanmathsRnSdkModule.kt)
|
|
78
|
+
on Android, `BMSafely()` in
|
|
79
|
+
[`ios/BooleanmathsRnSdk.mm`](ios/BooleanmathsRnSdk.mm)). Analytics should
|
|
80
|
+
never crash the host app.
|
|
81
|
+
|
|
82
|
+
On web the JavaScript gate is the whole story: a separate no-op implementation
|
|
83
|
+
([`src/BooleanMaths.tsx`](src/BooleanMaths.tsx)) is resolved by the bundler, so
|
|
84
|
+
shared code can call the SDK unconditionally without platform checks.
|
|
35
85
|
|
|
36
86
|
In development (`__DEV__`) you get **one** console warning per app launch, not
|
|
37
87
|
one per call.
|
|
38
88
|
|
|
39
|
-
###
|
|
89
|
+
### Optionally gate your own analytics code
|
|
40
90
|
|
|
41
91
|
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`:
|
|
92
|
+
you want to avoid dead work on web, skip the dev warning, or show different UI,
|
|
93
|
+
branch on `isSupported`:
|
|
44
94
|
|
|
45
95
|
```ts
|
|
46
96
|
import { BooleanMaths } from '@booleanmaths/booleanmaths-rn-sdk';
|
|
@@ -48,21 +98,12 @@ import { BooleanMaths } from '@booleanmaths/booleanmaths-rn-sdk';
|
|
|
48
98
|
if (BooleanMaths.isSupported) {
|
|
49
99
|
BooleanMaths.initialize(API_KEY, PIXEL_ID);
|
|
50
100
|
} else {
|
|
51
|
-
//
|
|
101
|
+
// Web, or a native install that needs rebuilding. Fall back or do nothing.
|
|
52
102
|
}
|
|
53
103
|
```
|
|
54
104
|
|
|
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).
|
|
105
|
+
`isSupported` is `true` on Android and iOS. It is a plain boolean, evaluated at
|
|
106
|
+
module load — cheap to read as often as you like.
|
|
66
107
|
|
|
67
108
|
---
|
|
68
109
|
|
|
@@ -78,14 +119,17 @@ Then rebuild the native app — a Metro reload is **not** enough:
|
|
|
78
119
|
|
|
79
120
|
```sh
|
|
80
121
|
npx react-native run-android
|
|
122
|
+
# and/or
|
|
123
|
+
cd ios && pod install && cd .. && npx react-native run-ios
|
|
81
124
|
```
|
|
82
125
|
|
|
83
126
|
### Requirements
|
|
84
127
|
|
|
85
128
|
- React Native **0.80+** with the **New Architecture** enabled (this is a
|
|
86
129
|
TurboModule)
|
|
87
|
-
- Android
|
|
88
|
-
|
|
130
|
+
- **Android:** `minSdkVersion` 24 or higher (the native SDK requires it),
|
|
131
|
+
`compileSdkVersion 36`, Java 17
|
|
132
|
+
- **iOS:** deployment target **15.1** or higher, and **Xcode 16+**
|
|
89
133
|
|
|
90
134
|
### Android
|
|
91
135
|
|
|
@@ -98,20 +142,44 @@ To pin a different native SDK version, set this in your app's **root**
|
|
|
98
142
|
```gradle
|
|
99
143
|
buildscript {
|
|
100
144
|
ext {
|
|
101
|
-
BooleanmathsRnSdk_bmSdkVersion = "1.0.
|
|
145
|
+
BooleanmathsRnSdk_bmSdkVersion = "1.0.12"
|
|
102
146
|
}
|
|
103
147
|
}
|
|
104
148
|
```
|
|
105
149
|
|
|
150
|
+
> ⚠️ **1.0.10 is the floor.** The wrapper calls
|
|
151
|
+
> `BooleanMathsSDK.initialize(context, apiKey, pixelId, isDebug)`, and the
|
|
152
|
+
> four-argument overload only exists from 1.0.10. Pinning anything older fails
|
|
153
|
+
> the Kotlin compile with an unresolved-overload error rather than degrading at
|
|
154
|
+
> runtime.
|
|
155
|
+
|
|
106
156
|
### iOS
|
|
107
157
|
|
|
108
|
-
|
|
109
|
-
compiles and links, it just does not track anything.
|
|
158
|
+
Autolinking picks the module up; run `pod install` to pull in the native SDK.
|
|
110
159
|
|
|
111
160
|
```sh
|
|
112
161
|
cd ios && pod install
|
|
113
162
|
```
|
|
114
163
|
|
|
164
|
+
That resolves `BooleanMathsSDK` from CocoaPods Trunk. Points worth knowing:
|
|
165
|
+
|
|
166
|
+
- **Minimum deployment target is iOS 15.1**, matching React Native's own floor,
|
|
167
|
+
so adopting this SDK does not raise your app's minimum iOS version.
|
|
168
|
+
- **The pod is constrained to `~> 1.1`** — that is, `>= 1.1, < 2.0`. The floor
|
|
169
|
+
matters: version 1.0.0 is still published with an iOS 17.0 floor, and
|
|
170
|
+
resolving to it would break the install for apps below iOS 17. Confirm your
|
|
171
|
+
`Podfile.lock` shows `BooleanMathsSDK (1.1.0)` or newer.
|
|
172
|
+
- **The SDK ships as a closed-source, vendored *dynamic* XCFramework.**
|
|
173
|
+
CocoaPods embeds and re-signs it with your app's identity. If you use
|
|
174
|
+
`use_frameworks!`, both `:linkage => :static` and `:linkage => :dynamic`
|
|
175
|
+
are supported.
|
|
176
|
+
- **No privacy manifest work needed.** The SDK bundles its own
|
|
177
|
+
`PrivacyInfo.xcprivacy` inside the XCFramework, so App Store
|
|
178
|
+
privacy-manifest requirements are covered by the pod.
|
|
179
|
+
- **Xcode 16+ is required.** The SDK's `.swiftinterface` is emitted at Swift 6.
|
|
180
|
+
Library evolution is enabled, so it is not pinned to the exact Xcode that
|
|
181
|
+
built it — but the toolchain must understand Swift 6.
|
|
182
|
+
|
|
115
183
|
---
|
|
116
184
|
|
|
117
185
|
## Usage
|
|
@@ -122,6 +190,9 @@ import { BooleanMaths } from '@booleanmaths/booleanmaths-rn-sdk';
|
|
|
122
190
|
// Once, as early as possible — typically in your root component.
|
|
123
191
|
BooleanMaths.initialize('YOUR_API_KEY', 'YOUR_PIXEL_ID');
|
|
124
192
|
|
|
193
|
+
// Or, to mark this build's events as development rather than production:
|
|
194
|
+
BooleanMaths.initialize('YOUR_API_KEY', 'YOUR_PIXEL_ID', __DEV__);
|
|
195
|
+
|
|
125
196
|
// Anywhere afterwards.
|
|
126
197
|
BooleanMaths.trackEvent('purchase', {
|
|
127
198
|
orderId: 'ORD-1024',
|
|
@@ -144,11 +215,51 @@ ignored.
|
|
|
144
215
|
|
|
145
216
|
## API
|
|
146
217
|
|
|
147
|
-
### `BooleanMaths.initialize(apiKey, pixelId): void`
|
|
218
|
+
### `BooleanMaths.initialize(apiKey, pixelId, isDebug?): void`
|
|
219
|
+
|
|
220
|
+
Initializes the native SDK. Also registers this wrapper with the native SDK
|
|
221
|
+
**before** initializing, so that even the automatic `FirstOpen` and `app_opened`
|
|
222
|
+
events emitted during initialization already carry
|
|
223
|
+
`wrapper_type: "react-native"` and the wrapper version. That version is read
|
|
224
|
+
from `package.json` on both platforms, so it cannot drift from the published npm
|
|
225
|
+
version.
|
|
226
|
+
|
|
227
|
+
On Android it additionally forwards the launch intent (see
|
|
228
|
+
[Deep links](#deep-links-and-notification-attribution)); there is no iOS
|
|
229
|
+
equivalent.
|
|
230
|
+
|
|
231
|
+
`isDebug` defaults to `false`. See [Debug mode](#debug-mode) below.
|
|
232
|
+
|
|
233
|
+
### Debug mode
|
|
234
|
+
|
|
235
|
+
Passing `isDebug: true` does two things on both platforms:
|
|
236
|
+
|
|
237
|
+
1. **Every event this process tracks is stamped `environment: "development"`**
|
|
238
|
+
instead of `"production"`, in the `setup` block of the request payload.
|
|
239
|
+
2. **The native SDK's verbose logging is switched on.** Warnings and errors —
|
|
240
|
+
"SDK is already initialized", network failures, malformed intents — are
|
|
241
|
+
logged *regardless* of this flag, so a broken integration is still
|
|
242
|
+
diagnosable in a production build. Only the routine per-event chatter
|
|
243
|
+
("Persisted event", "Sending payload") is gated.
|
|
244
|
+
|
|
245
|
+
The flag is recorded **per event, when the event is queued** — not when the
|
|
246
|
+
queue is flushed. An event written by a debug build therefore stays marked as
|
|
247
|
+
development even if it only reaches the network later, after an app restart.
|
|
248
|
+
Both platforms migrated their local queue schemas for this (Room v6 → v7 on
|
|
249
|
+
Android, SQLite v1 → v2 on iOS); the migration is automatic and no events are
|
|
250
|
+
lost.
|
|
148
251
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
252
|
+
The wrapper deliberately **does not** default this to `__DEV__`. Which
|
|
253
|
+
environment events land in is a backend-routing decision, not a bundler one —
|
|
254
|
+
release builds pointed at a development pixel are a normal QA setup. Pass
|
|
255
|
+
`__DEV__` yourself if that is the behaviour you want:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
BooleanMaths.initialize(API_KEY, PIXEL_ID, __DEV__);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Requires native SDK `bm-sdk` **1.0.10+** on Android and `BooleanMathsSDK`
|
|
262
|
+
**1.1.0+** on iOS; both are the versions this wrapper depends on.
|
|
152
263
|
|
|
153
264
|
### `BooleanMaths.trackEvent(name, properties?): void`
|
|
154
265
|
|
|
@@ -157,14 +268,19 @@ numbers, booleans, nested objects, and arrays.
|
|
|
157
268
|
|
|
158
269
|
### `BooleanMaths.handleIntent(): void`
|
|
159
270
|
|
|
160
|
-
**Android only
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
271
|
+
**Android only.** Forwards the current Activity's intent to the native SDK so
|
|
272
|
+
ad deep links, app links and push-notification campaign data are attributed.
|
|
273
|
+
Safe to call repeatedly — the native SDK de-duplicates intents it has already
|
|
274
|
+
seen.
|
|
164
275
|
|
|
165
276
|
This is the single entry point for every kind of launch intent; native SDK
|
|
166
277
|
1.0.9 unified them behind one method.
|
|
167
278
|
|
|
279
|
+
**A no-op on iOS**, and safe to call there — it logs one dev-mode notice and
|
|
280
|
+
returns. The reason is scope, not platform support: intents are an Android
|
|
281
|
+
concept, and iOS deep links / universal links are deferred to a later release.
|
|
282
|
+
You do not need to branch on platform before calling it.
|
|
283
|
+
|
|
168
284
|
### `BooleanMaths.handleNotificationIntent(): void`
|
|
169
285
|
|
|
170
286
|
Deprecated alias of `handleIntent()`, kept so existing callers keep working.
|
|
@@ -174,16 +290,26 @@ behavioural difference — prefer `handleIntent()` in new code.
|
|
|
174
290
|
### `BooleanMaths.getHelloMessage(): string | null`
|
|
175
291
|
|
|
176
292
|
Bridge smoke test. Returns the native SDK's hello string, or `null` where there
|
|
177
|
-
is no native SDK.
|
|
293
|
+
is no native SDK. The string comes from the native SDK itself on both platforms
|
|
294
|
+
(not from this wrapper), so a correct value also proves the native artifact
|
|
295
|
+
actually linked — which on iOS is the quickest way to confirm the XCFramework
|
|
296
|
+
was embedded.
|
|
178
297
|
|
|
179
298
|
### `BooleanMaths.isSupported: boolean`
|
|
180
299
|
|
|
181
|
-
`true` only where a real native SDK is linked
|
|
300
|
+
`true` only where a real native SDK is linked — Android and iOS. `false` on
|
|
301
|
+
web, and on any platform where the native module failed to resolve (typically a
|
|
302
|
+
JS install without a native rebuild).
|
|
182
303
|
|
|
183
304
|
---
|
|
184
305
|
|
|
185
306
|
## Deep links and notification attribution
|
|
186
307
|
|
|
308
|
+
> **Android only.** This entire section does not apply to iOS, where
|
|
309
|
+
> `handleIntent()` is a no-op and no `DeepLinkClick` / `NotificationClick`
|
|
310
|
+
> events are produced. The code below is still safe to run unchanged on iOS —
|
|
311
|
+
> the calls simply do nothing.
|
|
312
|
+
|
|
187
313
|
The native SDK registers its `ActivityLifecycleCallbacks` **inside**
|
|
188
314
|
`initialize()`. In a React Native app that runs long after `MainActivity`'s
|
|
189
315
|
`onActivityCreated` has already fired, so **the intent that cold-started your
|
|
@@ -277,19 +403,27 @@ intent without `ACTION_VIEW` takes the `NotificationClick` path instead — see
|
|
|
277
403
|
|
|
278
404
|
The native SDK tracks these without any call from you:
|
|
279
405
|
|
|
280
|
-
| Event | When |
|
|
281
|
-
| :---- | :--- |
|
|
282
|
-
| `app_opened` | First Activity creation / SDK initialization |
|
|
283
|
-
| `FirstOpen` | Once per install
|
|
284
|
-
| `DeepLinkClick` | An `ACTION_VIEW` intent is handled — ad deep links and app links. Campaign fields nest under `data.link` |
|
|
285
|
-
| `NotificationClick` | Any other intent carrying campaign data is handled. Fields nest under `data.notification` |
|
|
406
|
+
| Event | When | Android | iOS |
|
|
407
|
+
| :---- | :--- | :-----: | :-: |
|
|
408
|
+
| `app_opened` | First Activity creation / SDK initialization | ✅ | ✅ |
|
|
409
|
+
| `FirstOpen` | Once per install | ✅ with Google Play Install Referrer attribution | ✅ but `data` is `{}` |
|
|
410
|
+
| `DeepLinkClick` | An `ACTION_VIEW` intent is handled — ad deep links and app links. Campaign fields nest under `data.link` | ✅ | ❌ |
|
|
411
|
+
| `NotificationClick` | Any other intent carrying campaign data is handled. Fields nest under `data.notification` | ✅ | ❌ |
|
|
412
|
+
|
|
413
|
+
On both platforms `FirstOpen` precedes `app_opened`, so a new install's stream
|
|
414
|
+
reads in order. `FirstOpen` is PascalCase on the wire on purpose — the two
|
|
415
|
+
platforms match byte-for-byte and backend install reporting keys off that exact
|
|
416
|
+
string.
|
|
286
417
|
|
|
287
418
|
Visitor ID and session ID (30-minute timeout) are generated and persisted
|
|
288
|
-
natively
|
|
289
|
-
|
|
290
|
-
|
|
419
|
+
natively on both platforms.
|
|
420
|
+
|
|
421
|
+
**Attribution is Android-only.** Campaign data from a handled intent is
|
|
422
|
+
persisted by `AttributionManager` and attached to **every subsequent event** as
|
|
423
|
+
`data.attribution`. iOS produces no `data.attribution` block, since it handles
|
|
424
|
+
no intents; Apple Search Ads attribution is deferred to a later release.
|
|
291
425
|
|
|
292
|
-
### What counts as a campaign intent (native SDK v1.0.
|
|
426
|
+
### What counts as a campaign intent (native SDK v1.0.12)
|
|
293
427
|
|
|
294
428
|
`handleIntent` ignores an intent when **all** of these hold, which is what a
|
|
295
429
|
plain launcher tap looks like:
|
|
@@ -320,7 +454,34 @@ de-duplicated in a `WeakHashMap`, so re-forwarding the same intent is a no-op.
|
|
|
320
454
|
|
|
321
455
|
## How properties cross the bridge
|
|
322
456
|
|
|
323
|
-
|
|
457
|
+
Each platform normalizes `properties` at the native boundary before handing it
|
|
458
|
+
to the SDK. The rules differ because the two SDKs serialize differently — the
|
|
459
|
+
observable payload is the same in the cases that matter.
|
|
460
|
+
|
|
461
|
+
| Input | Android | iOS |
|
|
462
|
+
| :---- | :------ | :-- |
|
|
463
|
+
| `null` / `undefined` object value | Key dropped | Key dropped |
|
|
464
|
+
| `null` inside an array | Preserved (indices must not shift) | Preserved |
|
|
465
|
+
| Nested objects and arrays | Recursed | Recursed |
|
|
466
|
+
| Whole numbers (`3`) | Coerced to integer — see the Gson caveat below | No action needed; `JSONSerialization` already emits `3`, not `3.0` |
|
|
467
|
+
| Fractions (`999.5`) | Untouched | Untouched |
|
|
468
|
+
| Booleans | Untouched | Untouched |
|
|
469
|
+
| **`NaN` / `Infinity`** | Passed through as-is | **Key dropped** — see below |
|
|
470
|
+
|
|
471
|
+
### iOS: why `NaN` is dropped rather than passed through
|
|
472
|
+
|
|
473
|
+
This one is load-bearing, not cosmetic. The iOS SDK guards its writes with
|
|
474
|
+
`JSONSerialization.isValidJSONObject(...)` and **silently discards the payload**
|
|
475
|
+
when that returns false. A JS `NaN` or `Infinity` arrives as a non-finite
|
|
476
|
+
`NSNumber` and invalidates the whole object — so a single bad property value
|
|
477
|
+
could discard the event, or the entire outgoing batch. The wrapper strips those
|
|
478
|
+
keys at the boundary so the rest of the event survives.
|
|
479
|
+
|
|
480
|
+
`NSDate` and anything else not JSON-representable is dropped for the same
|
|
481
|
+
reason. Everything is handled recursively, so a `NaN` nested three objects deep
|
|
482
|
+
costs you that one key and nothing else.
|
|
483
|
+
|
|
484
|
+
### Android normalization
|
|
324
485
|
|
|
325
486
|
- **Integral numbers are converted to integers.** React Native passes every JS
|
|
326
487
|
number across the bridge as a `Double`, so `{ count: 3 }` would otherwise be
|
|
@@ -332,7 +493,7 @@ Two normalizations happen on the Android side, both worth knowing:
|
|
|
332
493
|
{ "value": 2499, "items": [{ "quantity": 2, "price": 999.5 }] }
|
|
333
494
|
```
|
|
334
495
|
|
|
335
|
-
> ⚠️ **Known native SDK limitation (still present in v1.0.
|
|
496
|
+
> ⚠️ **Known native SDK limitation (still present in v1.0.12).** The integers do *not* survive
|
|
336
497
|
> to the wire. `EventDispatcher` re-reads the stored payload with
|
|
337
498
|
> `gson.fromJson(properties, Map::class.java)`, and Gson coerces every number
|
|
338
499
|
> in a raw `Map` to `Double` — so the request body ends up with `2499.0` and
|
|
@@ -346,28 +507,50 @@ Two normalizations happen on the Android side, both worth knowing:
|
|
|
346
507
|
serializes with Gson, which omits null map values anyway, so the emitted JSON
|
|
347
508
|
is identical. Nulls **inside arrays** are preserved so indices do not shift.
|
|
348
509
|
|
|
349
|
-
Failures in the native bridge are logged
|
|
350
|
-
|
|
510
|
+
Failures in the native bridge are logged rather than thrown into JavaScript —
|
|
511
|
+
analytics should never crash the host app. Android logs to
|
|
512
|
+
`adb logcat -s BooleanmathsRnSdk`; iOS logs through `RCTLogError`, visible in
|
|
513
|
+
Xcode's console and Metro.
|
|
351
514
|
|
|
352
515
|
---
|
|
353
516
|
|
|
354
517
|
## Troubleshooting
|
|
355
518
|
|
|
356
519
|
**No events arriving.** Confirm `BooleanMaths.isSupported` is `true`, then check
|
|
357
|
-
`adb logcat -s BooleanMathsSDK:D BooleanmathsRnSdk:D
|
|
358
|
-
|
|
520
|
+
the native logs — `adb logcat -s BooleanMathsSDK:D BooleanmathsRnSdk:D` on
|
|
521
|
+
Android, or Xcode's console on iOS. The native SDK logs each persisted event and
|
|
522
|
+
the full payload it sends.
|
|
359
523
|
|
|
360
524
|
**Warning: "native module could not be found".** The JS installed but the native
|
|
361
|
-
side did not. Rebuild the app (`npx react-native run-android
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
**
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
525
|
+
side did not. Rebuild the app (`npx react-native run-android`, or `pod install`
|
|
526
|
+
followed by `npx react-native run-ios`) rather than just reloading Metro.
|
|
527
|
+
|
|
528
|
+
**iOS: crash at launch with a dyld "Library not loaded" / "image not found"
|
|
529
|
+
error naming `BooleanMathsSDK`.** The XCFramework is a *dynamic* framework and
|
|
530
|
+
was not embedded. Confirm the `[CP] Embed Pods Frameworks` build phase exists on
|
|
531
|
+
your app target, then `pod deintegrate && pod install`. This is the one iOS
|
|
532
|
+
failure mode that builds cleanly and only shows up at runtime.
|
|
533
|
+
|
|
534
|
+
**iOS: `getHelloMessage()` returns an empty string.** The bridge resolved but the
|
|
535
|
+
native SDK call failed — check the Xcode console for a
|
|
536
|
+
`BooleanMaths getHelloMessage failed` error. An empty string here specifically
|
|
537
|
+
means the XCFramework did not link correctly.
|
|
538
|
+
|
|
539
|
+
**iOS: `pod install` fails to resolve `BooleanMathsSDK`.** Run
|
|
540
|
+
`pod repo update`, and confirm your app's deployment target is 15.1 or higher.
|
|
541
|
+
If the lockfile pinned `1.0.0`, delete that entry and reinstall — 1.0.0 carries
|
|
542
|
+
an iOS 17.0 floor.
|
|
543
|
+
|
|
544
|
+
**Events show a delay.** By design on both platforms. Each `trackEvent`
|
|
545
|
+
enqueues an immediate sync attempt; Android's WorkManager also runs a periodic
|
|
546
|
+
15-minute batch job and requires network connectivity, and iOS flushes when the
|
|
547
|
+
app backgrounds.
|
|
548
|
+
|
|
549
|
+
**Deep link not attributed.** Android only — see
|
|
550
|
+
[Deep links](#deep-links-and-notification-attribution); the launch intent needs
|
|
551
|
+
explicit forwarding, and `singleTask` activities need `setIntent`. On iOS deep
|
|
552
|
+
link attribution is not implemented at all
|
|
553
|
+
([what iOS supports](#what-ios-supports)).
|
|
371
554
|
|
|
372
555
|
---
|
|
373
556
|
|
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.12"
|
|
10
10
|
]
|
|
11
11
|
|
|
12
12
|
ext.getExtOrDefault = { prop ->
|
|
@@ -8,17 +8,22 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
8
8
|
class BooleanmathsRnSdkModule(reactContext: ReactApplicationContext) :
|
|
9
9
|
NativeBooleanmathsRnSdkSpec(reactContext) {
|
|
10
10
|
|
|
11
|
-
override fun initializeSdk(apiKey: String, pixelId: String) {
|
|
11
|
+
override fun initializeSdk(apiKey: String, pixelId: String, isDebug: Boolean) {
|
|
12
12
|
safely("initialize") {
|
|
13
13
|
// Stamp the wrapper identity *before* initialize() so the automatic
|
|
14
14
|
// `app_opened` and `FirstOpen` events that the native SDK tracks during
|
|
15
15
|
// initialization already carry wrapper_type / wrapper_version.
|
|
16
16
|
applyWrapperConfig()
|
|
17
17
|
|
|
18
|
+
// `isDebug` is passed explicitly rather than left to the native default:
|
|
19
|
+
// it decides the `environment` field ("development" / "production") that
|
|
20
|
+
// every event carries, and it gates the native SDK's verbose logging.
|
|
21
|
+
// The JS wrapper always supplies it, defaulting to false.
|
|
18
22
|
BooleanMathsSDK.initialize(
|
|
19
23
|
reactApplicationContext.applicationContext,
|
|
20
24
|
apiKey,
|
|
21
|
-
pixelId
|
|
25
|
+
pixelId,
|
|
26
|
+
isDebug
|
|
22
27
|
)
|
|
23
28
|
|
|
24
29
|
// ...and again afterwards, because the native SDK can only persist the
|
|
@@ -0,0 +1,151 @@
|
|
|
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:isDebug:wrapperVersion:)
|
|
39
|
+
public static func initializeSdk(
|
|
40
|
+
apiKey: String,
|
|
41
|
+
pixelId: String,
|
|
42
|
+
isDebug: Bool,
|
|
43
|
+
wrapperVersion: String
|
|
44
|
+
) {
|
|
45
|
+
// Before `initialize`, not after: `initialize` itself emits `FirstOpen` and
|
|
46
|
+
// `app_opened` internally, and those must already carry wrapper_type /
|
|
47
|
+
// wrapper_version. Same ordering rationale as Android.
|
|
48
|
+
//
|
|
49
|
+
// Do NOT port Android's second, post-initialize call. Android needs it
|
|
50
|
+
// because its native SDK can only persist to SharedPreferences once it holds
|
|
51
|
+
// an application context, which it acquires inside initialize(). iOS has no
|
|
52
|
+
// such gap: `WrapperConfigStore.set` writes to UserDefaults unconditionally,
|
|
53
|
+
// and `initialize` calls `rehydrate()`, which fills each field only where it
|
|
54
|
+
// is still nil — so a value set earlier in the process outranks the
|
|
55
|
+
// persisted one. One call, before initialize, is correct and sufficient.
|
|
56
|
+
BooleanMaths.shared.setWrapperConfig(type: wrapperType, version: wrapperVersion)
|
|
57
|
+
|
|
58
|
+
// `isDebug` is passed explicitly rather than left to the SDK's default: it
|
|
59
|
+
// decides the `environment` field ("development" / "production") stamped on
|
|
60
|
+
// every event, and it gates the SDK's verbose logging. The JS wrapper always
|
|
61
|
+
// supplies it, defaulting to false. Same contract as Android.
|
|
62
|
+
BooleanMaths.shared.initialize(apiKey: apiKey, pixelId: pixelId, isDebug: isDebug)
|
|
63
|
+
|
|
64
|
+
// There is no iOS equivalent of Android's forwardCurrentIntent(): deep links
|
|
65
|
+
// are deferred on iOS, so nothing is forwarded here. See `handleIntent` in
|
|
66
|
+
// BooleanmathsRnSdk.mm.
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// Records a custom event. `properties` is sanitized first — see
|
|
70
|
+
/// `sanitized(value:allowNull:)` for why that is load-bearing.
|
|
71
|
+
///
|
|
72
|
+
/// `@MainActor` because `BooleanMaths.track` is main-actor isolated.
|
|
73
|
+
@MainActor
|
|
74
|
+
@objc(trackEventWithName:properties:)
|
|
75
|
+
public static func trackEvent(name: String, properties: [String: Any]?) {
|
|
76
|
+
BooleanMaths.shared.track(name, properties: sanitized(dictionary: properties ?? [:]))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Bridge smoke test. Deliberately not main-actor isolated: the TurboModule
|
|
80
|
+
/// method is synchronous and returns the value directly, so it must not hop
|
|
81
|
+
/// threads. `BooleanMaths.getHelloMessage()` is `nonisolated static` in the SDK
|
|
82
|
+
/// specifically to allow this.
|
|
83
|
+
///
|
|
84
|
+
/// A real SDK call rather than a wrapper-side literal, so it also proves the
|
|
85
|
+
/// XCFramework actually linked.
|
|
86
|
+
@objc(getHelloMessage)
|
|
87
|
+
public static func getHelloMessage() -> String {
|
|
88
|
+
BooleanMaths.getHelloMessage()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// MARK: - Properties sanitization
|
|
92
|
+
|
|
93
|
+
// `EventDispatcher` and `EventStore` both guard on
|
|
94
|
+
// `JSONSerialization.isValidJSONObject(...)` and silently drop the payload when
|
|
95
|
+
// it returns false. A JS `NaN` or `Infinity` arrives here as a non-finite
|
|
96
|
+
// NSNumber and makes the whole object invalid — so one bad property value can
|
|
97
|
+
// discard the event, or the entire outgoing batch. Android guards the same case
|
|
98
|
+
// in `normalizeValue`.
|
|
99
|
+
|
|
100
|
+
private static func sanitized(dictionary: [String: Any]) -> [String: Any] {
|
|
101
|
+
var result = [String: Any](minimumCapacity: dictionary.count)
|
|
102
|
+
|
|
103
|
+
for (key, value) in dictionary {
|
|
104
|
+
guard let clean = sanitized(value: value, allowNull: false) else { continue }
|
|
105
|
+
result[key] = clean
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return result
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Nulls are preserved inside arrays — dropping them would shift indices.
|
|
112
|
+
/// Android preserves them deliberately for the same reason.
|
|
113
|
+
private static func sanitized(array: [Any]) -> [Any] {
|
|
114
|
+
array.map { sanitized(value: $0, allowNull: true) ?? NSNull() }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private static func sanitized(value: Any, allowNull: Bool) -> Any? {
|
|
118
|
+
switch value {
|
|
119
|
+
case is NSNull:
|
|
120
|
+
// Dropped as an object value (matching Android, whose Gson omits null map
|
|
121
|
+
// values anyway), kept inside an array.
|
|
122
|
+
return allowNull ? NSNull() : nil
|
|
123
|
+
|
|
124
|
+
case let number as NSNumber:
|
|
125
|
+
// Must be tested before any `as Bool` cast: `NSNumber(1) as? Bool`
|
|
126
|
+
// succeeds under Swift bridging, so checking Bool first would silently
|
|
127
|
+
// turn `{ count: 1 }` into `{ count: true }`. Booleans arrive from React
|
|
128
|
+
// Native as CFBoolean, match here, and serialize as true/false unchanged.
|
|
129
|
+
//
|
|
130
|
+
// No whole-number coercion: Android's `toWholeNumberOrSelf` fixes a
|
|
131
|
+
// Gson-specific artifact, whereas JSONSerialization already emits `3`
|
|
132
|
+
// rather than `3.0`.
|
|
133
|
+
return number.doubleValue.isFinite ? number : nil
|
|
134
|
+
|
|
135
|
+
case let string as String:
|
|
136
|
+
return string
|
|
137
|
+
|
|
138
|
+
case let nested as [String: Any]:
|
|
139
|
+
return sanitized(dictionary: nested)
|
|
140
|
+
|
|
141
|
+
case let nested as [Any]:
|
|
142
|
+
return sanitized(array: nested)
|
|
143
|
+
|
|
144
|
+
default:
|
|
145
|
+
// NSDate and anything else not JSON-representable, plus dictionaries with
|
|
146
|
+
// non-String keys. React Native never produces these from a JS object, but
|
|
147
|
+
// passing one through would invalidate the payload.
|
|
148
|
+
return nil
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
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,50 +2,105 @@
|
|
|
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
|
-
- (void)initializeSdk:(NSString *)apiKey pixelId:(NSString *)pixelId
|
|
50
|
+
- (void)initializeSdk:(NSString *)apiKey pixelId:(NSString *)pixelId isDebug:(BOOL)isDebug
|
|
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
|
+
isDebug:isDebug
|
|
62
|
+
wrapperVersion:wrapperVersion];
|
|
63
|
+
});
|
|
64
|
+
});
|
|
24
65
|
}
|
|
25
66
|
|
|
26
67
|
- (void)trackEvent:(NSString *)name properties:(NSDictionary *)properties
|
|
27
68
|
{
|
|
28
|
-
|
|
69
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
70
|
+
BMSafely(@"trackEvent", ^{
|
|
71
|
+
[BMBooleanMathsBridge trackEventWithName:name properties:properties];
|
|
72
|
+
});
|
|
73
|
+
});
|
|
29
74
|
}
|
|
30
75
|
|
|
31
76
|
- (void)handleIntent
|
|
32
77
|
{
|
|
33
|
-
//
|
|
34
|
-
|
|
78
|
+
// Deliberately still a no-op. Intents are an Android concept, and iOS deep
|
|
79
|
+
// links / universal links are deferred — their absence here is scope, not a
|
|
80
|
+
// missing implementation.
|
|
81
|
+
BMWarnIntentUnsupportedOnce();
|
|
35
82
|
}
|
|
36
83
|
|
|
37
84
|
- (void)handleNotificationIntent
|
|
38
85
|
{
|
|
39
86
|
// Alias of handleIntent, mirroring the Android bridge.
|
|
40
|
-
|
|
87
|
+
BMWarnIntentUnsupportedOnce();
|
|
41
88
|
}
|
|
42
89
|
|
|
43
90
|
- (NSString *)getHelloMessage
|
|
44
91
|
{
|
|
45
|
-
|
|
92
|
+
// No dispatch: this is synchronous and returns a value, so it must not hop
|
|
93
|
+
// threads. The SDK's getHelloMessage() is `nonisolated static` to allow it.
|
|
94
|
+
@try {
|
|
95
|
+
return [BMBooleanMathsBridge getHelloMessage];
|
|
96
|
+
} @catch (NSException *exception) {
|
|
97
|
+
RCTLogError(@"[@booleanmaths/booleanmaths-rn-sdk] BooleanMaths getHelloMessage failed: %@", exception);
|
|
46
98
|
|
|
47
|
-
|
|
48
|
-
|
|
99
|
+
// Empty rather than a plausible-looking message: this method exists to prove
|
|
100
|
+
// the native SDK linked, so a failure must read as a failure. The codegen
|
|
101
|
+
// spec declares a non-optional string, so it cannot be nil.
|
|
102
|
+
return @"";
|
|
103
|
+
}
|
|
49
104
|
}
|
|
50
105
|
|
|
51
106
|
- (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","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;
|
|
1
|
+
{"version":3,"names":["hasWarned","warnOnce","__DEV__","console","warn","BooleanMaths","isSupported","initialize","_apiKey","_pixelId","_isDebug","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,EAAEC,QAAkB,EAAQ;IACtET,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDU,UAAUA,CAACC,KAAa,EAAEC,WAAyC,EAAQ;IACzEZ,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDa,YAAYA,CAAA,EAAS;IACnBb,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDc,wBAAwBA,CAAA,EAAS;IAC/Bd,QAAQ,CAAC,CAAC;EACZ,CAAC;EAEDe,eAAeA,CAAA,EAAkB;IAC/Bf,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,7 +27,7 @@ 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.');
|
|
@@ -44,12 +48,16 @@ function forwardCurrentIntent() {
|
|
|
44
48
|
}
|
|
45
49
|
export const BooleanMaths = {
|
|
46
50
|
isSupported,
|
|
47
|
-
initialize(apiKey, pixelId) {
|
|
51
|
+
initialize(apiKey, pixelId, isDebug = false) {
|
|
48
52
|
if (!isSupported) {
|
|
49
53
|
warnOnce();
|
|
50
54
|
return;
|
|
51
55
|
}
|
|
52
|
-
|
|
56
|
+
|
|
57
|
+
// Coerced rather than passed through: the codegen spec declares a
|
|
58
|
+
// non-optional boolean, and a truthy non-boolean from untyped JS would
|
|
59
|
+
// arrive at the native side as a type mismatch rather than a flag.
|
|
60
|
+
NativeBooleanmathsRnSdk.initializeSdk(apiKey, pixelId, Boolean(isDebug));
|
|
53
61
|
},
|
|
54
62
|
trackEvent(name, properties = {}) {
|
|
55
63
|
if (!isSupported) {
|
|
@@ -1 +1 @@
|
|
|
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,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","isDebug","initializeSdk","Boolean","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,EAAEC,OAAgB,GAAG,KAAK,EAAQ;IAC1E,IAAI,CAACZ,WAAW,EAAE;MAChBE,QAAQ,CAAC,CAAC;MACV;IACF;;IAEA;IACA;IACA;IACAP,uBAAuB,CAAEkB,aAAa,CAACH,MAAM,EAAEC,OAAO,EAAEG,OAAO,CAACF,OAAO,CAAC,CAAC;EAC3E,CAAC;EAEDG,UAAUA,CAACC,IAAY,EAAEC,UAAuC,GAAG,CAAC,CAAC,EAAQ;IAC3E,IAAI,CAACjB,WAAW,EAAE;MAChBE,QAAQ,CAAC,CAAC;MACV;IACF;IAEAP,uBAAuB,CAAEoB,UAAU,CAACC,IAAI,EAAEC,UAAU,CAAC;EACvD,CAAC;EAEDV,YAAY,EAAED,oBAAoB;EAElCY,wBAAwB,EAAEZ,oBAAoB;EAE9Ca,eAAeA,CAAA,EAAkB;IAC/B,IAAI,CAACnB,WAAW,EAAE;MAChBE,QAAQ,CAAC,CAAC;MACV,OAAO,IAAI;IACb;IAEA,OAAOP,uBAAuB,CAAEwB,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;AA2BpE;AACA;AACA;AACA;AACA,eAAeA,mBAAmB,CAACC,GAAG,CAAO,mBAAmB,CAAC","ignoreList":[]}
|
|
@@ -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,eAoC1B,CAAC"}
|
|
@@ -5,8 +5,13 @@ export interface Spec extends TurboModule {
|
|
|
5
5
|
* already taken by `NativeModule.initialize()` on Android and by
|
|
6
6
|
* `+[NSObject initialize]` on iOS. The public JS API exposes this as
|
|
7
7
|
* `BooleanMaths.initialize()`.
|
|
8
|
+
*
|
|
9
|
+
* `isDebug` is required here even though it is optional on the public API —
|
|
10
|
+
* codegen has no notion of a default, so `BooleanMaths.initialize` always
|
|
11
|
+
* passes an explicit boolean. Same arrangement as `trackEvent`'s
|
|
12
|
+
* `properties`.
|
|
8
13
|
*/
|
|
9
|
-
initializeSdk(apiKey: string, pixelId: string): void;
|
|
14
|
+
initializeSdk(apiKey: string, pixelId: string, isDebug: boolean): void;
|
|
10
15
|
trackEvent(name: string, properties: Object): void;
|
|
11
16
|
/** Android only. No-op elsewhere. */
|
|
12
17
|
handleIntent(): void;
|
|
@@ -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
|
|
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;;;;;;;;;;OAUG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACvE,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"}
|
|
@@ -15,8 +15,21 @@ export interface BooleanMathsApi {
|
|
|
15
15
|
*
|
|
16
16
|
* Calling this more than once is harmless — the native SDK ignores
|
|
17
17
|
* subsequent calls.
|
|
18
|
+
*
|
|
19
|
+
* @param isDebug Marks this process as a development build. Every event it
|
|
20
|
+
* tracks is stamped `environment: "development"` instead of `"production"`,
|
|
21
|
+
* and the native SDKs' verbose logging is switched on. Defaults to `false`.
|
|
22
|
+
*
|
|
23
|
+
* The flag is recorded per event when the event is queued, not when the
|
|
24
|
+
* queue is flushed, so events written by a debug build stay marked as
|
|
25
|
+
* development even if they only sync later.
|
|
26
|
+
*
|
|
27
|
+
* It is deliberately *not* derived from `__DEV__` inside the wrapper —
|
|
28
|
+
* where an event lands is a backend-routing decision, not a bundler one, and
|
|
29
|
+
* some teams want release builds pointed at development. Pass `__DEV__`
|
|
30
|
+
* yourself if that is the behaviour you want.
|
|
18
31
|
*/
|
|
19
|
-
initialize(apiKey: string, pixelId: string): void;
|
|
32
|
+
initialize(apiKey: string, pixelId: string, isDebug?: boolean): void;
|
|
20
33
|
/**
|
|
21
34
|
* Records a custom event. Events are persisted locally and synced in the
|
|
22
35
|
* background, so this never blocks and never throws on network failure.
|
|
@@ -28,10 +41,14 @@ export interface BooleanMathsApi {
|
|
|
28
41
|
* Forwards the current Activity's intent to the native SDK so ad deep links,
|
|
29
42
|
* app links and push-notification campaign data are attributed.
|
|
30
43
|
*
|
|
31
|
-
* This is the primary entry point for every kind of launch intent.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
44
|
+
* This is the primary entry point for every kind of launch intent. Call it
|
|
45
|
+
* from your deep-link handler; `initialize` already forwards the launch
|
|
46
|
+
* intent itself (see the README for why that needs special handling).
|
|
47
|
+
*
|
|
48
|
+
* **Android only.** A no-op on iOS — not because iOS is unsupported (event
|
|
49
|
+
* tracking works fully there), but because intents are an Android concept and
|
|
50
|
+
* iOS deep links / universal links are out of scope for now. Safe to call
|
|
51
|
+
* unconditionally from shared code; on iOS it logs one dev-mode notice.
|
|
35
52
|
*
|
|
36
53
|
* Safe to call repeatedly — the native SDK de-duplicates intents it has
|
|
37
54
|
* already processed.
|
|
@@ -47,12 +64,17 @@ export interface BooleanMathsApi {
|
|
|
47
64
|
handleNotificationIntent(): void;
|
|
48
65
|
/**
|
|
49
66
|
* Returns the native SDK's hello message, or `null` when there is no native
|
|
50
|
-
* SDK on this platform.
|
|
67
|
+
* SDK on this platform.
|
|
68
|
+
*
|
|
69
|
+
* Useful as a bridge smoke test: the string comes from the native SDK itself
|
|
70
|
+
* on both Android and iOS, so a correct value also proves the native artifact
|
|
71
|
+
* actually linked.
|
|
51
72
|
*/
|
|
52
73
|
getHelloMessage(): string | null;
|
|
53
74
|
/**
|
|
54
|
-
* `true` only on platforms where a real BooleanMaths native SDK is linked
|
|
55
|
-
*
|
|
75
|
+
* `true` only on platforms where a real BooleanMaths native SDK is linked —
|
|
76
|
+
* Android and iOS. `false` on web, and on any platform where the native
|
|
77
|
+
* module failed to resolve (typically a JS install without a native rebuild).
|
|
56
78
|
*/
|
|
57
79
|
readonly isSupported: boolean;
|
|
58
80
|
}
|
|
@@ -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
|
|
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;;;;;;;;;;;;;;;;;;;OAmBG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAErE;;;;;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.4.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
|
);
|
|
@@ -63,13 +70,16 @@ function forwardCurrentIntent(): void {
|
|
|
63
70
|
export const BooleanMaths: BooleanMathsApi = {
|
|
64
71
|
isSupported,
|
|
65
72
|
|
|
66
|
-
initialize(apiKey: string, pixelId: string): void {
|
|
73
|
+
initialize(apiKey: string, pixelId: string, isDebug: boolean = false): void {
|
|
67
74
|
if (!isSupported) {
|
|
68
75
|
warnOnce();
|
|
69
76
|
return;
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
// Coerced rather than passed through: the codegen spec declares a
|
|
80
|
+
// non-optional boolean, and a truthy non-boolean from untyped JS would
|
|
81
|
+
// arrive at the native side as a type mismatch rather than a flag.
|
|
82
|
+
NativeBooleanmathsRnSdk!.initializeSdk(apiKey, pixelId, Boolean(isDebug));
|
|
73
83
|
},
|
|
74
84
|
|
|
75
85
|
trackEvent(name: string, properties: BooleanMathsEventProperties = {}): void {
|
package/src/BooleanMaths.tsx
CHANGED
|
@@ -6,8 +6,13 @@ export interface Spec extends TurboModule {
|
|
|
6
6
|
* already taken by `NativeModule.initialize()` on Android and by
|
|
7
7
|
* `+[NSObject initialize]` on iOS. The public JS API exposes this as
|
|
8
8
|
* `BooleanMaths.initialize()`.
|
|
9
|
+
*
|
|
10
|
+
* `isDebug` is required here even though it is optional on the public API —
|
|
11
|
+
* codegen has no notion of a default, so `BooleanMaths.initialize` always
|
|
12
|
+
* passes an explicit boolean. Same arrangement as `trackEvent`'s
|
|
13
|
+
* `properties`.
|
|
9
14
|
*/
|
|
10
|
-
initializeSdk(apiKey: string, pixelId: string): void;
|
|
15
|
+
initializeSdk(apiKey: string, pixelId: string, isDebug: boolean): void;
|
|
11
16
|
trackEvent(name: string, properties: Object): void;
|
|
12
17
|
/** Android only. No-op elsewhere. */
|
|
13
18
|
handleIntent(): void;
|
package/src/types.ts
CHANGED
|
@@ -25,8 +25,21 @@ export interface BooleanMathsApi {
|
|
|
25
25
|
*
|
|
26
26
|
* Calling this more than once is harmless — the native SDK ignores
|
|
27
27
|
* subsequent calls.
|
|
28
|
+
*
|
|
29
|
+
* @param isDebug Marks this process as a development build. Every event it
|
|
30
|
+
* tracks is stamped `environment: "development"` instead of `"production"`,
|
|
31
|
+
* and the native SDKs' verbose logging is switched on. Defaults to `false`.
|
|
32
|
+
*
|
|
33
|
+
* The flag is recorded per event when the event is queued, not when the
|
|
34
|
+
* queue is flushed, so events written by a debug build stay marked as
|
|
35
|
+
* development even if they only sync later.
|
|
36
|
+
*
|
|
37
|
+
* It is deliberately *not* derived from `__DEV__` inside the wrapper —
|
|
38
|
+
* where an event lands is a backend-routing decision, not a bundler one, and
|
|
39
|
+
* some teams want release builds pointed at development. Pass `__DEV__`
|
|
40
|
+
* yourself if that is the behaviour you want.
|
|
28
41
|
*/
|
|
29
|
-
initialize(apiKey: string, pixelId: string): void;
|
|
42
|
+
initialize(apiKey: string, pixelId: string, isDebug?: boolean): void;
|
|
30
43
|
|
|
31
44
|
/**
|
|
32
45
|
* Records a custom event. Events are persisted locally and synced in the
|
|
@@ -40,10 +53,14 @@ export interface BooleanMathsApi {
|
|
|
40
53
|
* Forwards the current Activity's intent to the native SDK so ad deep links,
|
|
41
54
|
* app links and push-notification campaign data are attributed.
|
|
42
55
|
*
|
|
43
|
-
* This is the primary entry point for every kind of launch intent.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
56
|
+
* This is the primary entry point for every kind of launch intent. Call it
|
|
57
|
+
* from your deep-link handler; `initialize` already forwards the launch
|
|
58
|
+
* intent itself (see the README for why that needs special handling).
|
|
59
|
+
*
|
|
60
|
+
* **Android only.** A no-op on iOS — not because iOS is unsupported (event
|
|
61
|
+
* tracking works fully there), but because intents are an Android concept and
|
|
62
|
+
* iOS deep links / universal links are out of scope for now. Safe to call
|
|
63
|
+
* unconditionally from shared code; on iOS it logs one dev-mode notice.
|
|
47
64
|
*
|
|
48
65
|
* Safe to call repeatedly — the native SDK de-duplicates intents it has
|
|
49
66
|
* already processed.
|
|
@@ -61,13 +78,18 @@ export interface BooleanMathsApi {
|
|
|
61
78
|
|
|
62
79
|
/**
|
|
63
80
|
* Returns the native SDK's hello message, or `null` when there is no native
|
|
64
|
-
* SDK on this platform.
|
|
81
|
+
* SDK on this platform.
|
|
82
|
+
*
|
|
83
|
+
* Useful as a bridge smoke test: the string comes from the native SDK itself
|
|
84
|
+
* on both Android and iOS, so a correct value also proves the native artifact
|
|
85
|
+
* actually linked.
|
|
65
86
|
*/
|
|
66
87
|
getHelloMessage(): string | null;
|
|
67
88
|
|
|
68
89
|
/**
|
|
69
|
-
* `true` only on platforms where a real BooleanMaths native SDK is linked
|
|
70
|
-
*
|
|
90
|
+
* `true` only on platforms where a real BooleanMaths native SDK is linked —
|
|
91
|
+
* Android and iOS. `false` on web, and on any platform where the native
|
|
92
|
+
* module failed to resolve (typically a JS install without a native rebuild).
|
|
71
93
|
*/
|
|
72
94
|
readonly isSupported: boolean;
|
|
73
95
|
}
|