expo-facebook-analytics 1.0.6 โ†’ 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +37 -196
  2. package/android/CMakeLists.txt +16 -0
  3. package/android/build.gradle +36 -11
  4. package/android/src/main/cpp/cpp-adapter.cpp +14 -0
  5. package/android/src/main/java/com/nitrofbanalytics/FacebookAnalyticsPackage.kt +30 -0
  6. package/android/src/main/java/com/nitrofbanalytics/HybridFacebookAnalytics.kt +77 -8
  7. package/expo-facebook-analytics.podspec +1 -1
  8. package/ios/HybridFacebookAnalytics.swift +67 -9
  9. package/lib/index.d.ts +52 -0
  10. package/lib/index.js +101 -19
  11. package/lib/specs/FacebookAnalytics.nitro.d.ts +22 -0
  12. package/nitro.json +8 -2
  13. package/nitrogen/generated/android/NitroFBAnalyticsOnLoad.cpp +2 -2
  14. package/nitrogen/generated/android/c++/JHybridFacebookAnalyticsSpec.cpp +32 -0
  15. package/nitrogen/generated/android/c++/JHybridFacebookAnalyticsSpec.hpp +10 -2
  16. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitrofbanalytics/HybridFacebookAnalyticsSpec.kt +32 -0
  17. package/nitrogen/generated/ios/NitroFBAnalytics+autolinking.rb +2 -0
  18. package/nitrogen/generated/ios/c++/HybridFacebookAnalyticsSpecSwift.hpp +48 -0
  19. package/nitrogen/generated/ios/swift/HybridFacebookAnalyticsSpec.swift +8 -0
  20. package/nitrogen/generated/ios/swift/HybridFacebookAnalyticsSpec_cxx.swift +88 -0
  21. package/nitrogen/generated/shared/c++/HybridFacebookAnalyticsSpec.cpp +8 -0
  22. package/nitrogen/generated/shared/c++/HybridFacebookAnalyticsSpec.hpp +8 -0
  23. package/package.json +9 -7
  24. package/plugin/build/withFacebookAnalyticsIOS.d.ts +9 -0
  25. package/plugin/build/withFacebookAnalyticsIOS.js +56 -15
  26. package/react-native.config.js +9 -1
  27. package/src/index.ts +109 -19
  28. package/src/specs/FacebookAnalytics.nitro.ts +24 -0
package/README.md CHANGED
@@ -2,45 +2,21 @@
2
2
 
3
3
  High-performance Facebook Analytics for React Native using [Nitro Modules](https://github.com/mrousavy/nitro).
4
4
 
5
- ## Features
5
+ ๐Ÿ“– **[Documentation](https://jasperaelvoet.github.io/expo-facebook-analytics/)** ยท ๐Ÿงช **[Example app](./example)**
6
6
 
7
- - Direct native Facebook SDK integration via Nitro Modules (zero-copy bridge)
8
- - Full event logging, user identity management, and device identifier access
9
- - Expo config plugin for automatic native setup
10
- - TypeScript-first with predefined event and parameter constants
11
- - iOS 13+ and Android SDK 21+
7
+ > **Minimal by design.** This library wraps only the Facebook **App Events /
8
+ > Analytics** APIs โ€” event logging, user identity, and device identifiers. It is
9
+ > **not** a full Facebook SDK (no Login, Sharing, Graph API, or Gaming). If you
10
+ > only need analytics and conversion tracking, that's the point: a tiny, fast
11
+ > surface area built on a zero-overhead Nitro/JSI bridge to the native SDK.
12
12
 
13
13
  ## Installation
14
14
 
15
15
  ```sh
16
- bun add expo-facebook-analytics react-native-nitro-modules
16
+ npx expo install expo-facebook-analytics react-native-nitro-modules
17
17
  ```
18
18
 
19
- or
20
-
21
- ```sh
22
- npm install expo-facebook-analytics react-native-nitro-modules
23
- ```
24
-
25
- ### Peer Dependencies
26
-
27
- - `react-native` >= 0.71
28
- - `react-native-nitro-modules` >= 0.35.0
29
- - `expo` >= 55.0.0 _(optional, for config plugin)_
30
-
31
- ### iOS
32
-
33
- ```sh
34
- cd ios && pod install
35
- ```
36
-
37
- ### Android
38
-
39
- No additional steps required โ€” Gradle handles the Facebook SDK dependency automatically.
40
-
41
- ## Expo Setup
42
-
43
- Add the plugin to your `app.json` / `app.config.js`:
19
+ Add the config plugin to your `app.json`:
44
20
 
45
21
  ```json
46
22
  {
@@ -50,8 +26,7 @@ Add the plugin to your `app.json` / `app.config.js`:
50
26
  "expo-facebook-analytics",
51
27
  {
52
28
  "appID": "YOUR_FACEBOOK_APP_ID",
53
- "clientToken": "YOUR_FACEBOOK_CLIENT_TOKEN",
54
- "displayName": "Your App Name"
29
+ "clientToken": "YOUR_FACEBOOK_CLIENT_TOKEN"
55
30
  }
56
31
  ]
57
32
  ]
@@ -59,183 +34,49 @@ Add the plugin to your `app.json` / `app.config.js`:
59
34
  }
60
35
  ```
61
36
 
62
- ### Plugin Options
37
+ The module ships native code, so it requires a [development build](https://docs.expo.dev/develop/development-builds/introduction/) โ€” it can't run in Expo Go.
63
38
 
64
- | Option | Type | Default | Description |
65
- | ------------------------------- | --------------------- | ------------- | ------------------------------------------ |
66
- | `appID` | `string` | **required** | Facebook App ID |
67
- | `clientToken` | `string` | **required** | Facebook Client Token |
68
- | `displayName` | `string` | App name | Display name for Facebook |
69
- | `scheme` | `string` | `fb{appID}` | URL scheme for deep linking |
70
- | `isAutoInitEnabled` | `boolean` | `true` | Auto-initialize Facebook SDK |
71
- | `autoLogAppEventsEnabled` | `boolean` | `true` | Automatically log app events |
72
- | `advertiserIDCollectionEnabled` | `boolean` | `true` | Enable advertiser ID collection |
73
- | `iosUserTrackingPermission` | `string` \| `false` | ATT prompt | Custom tracking permission text or disable |
74
-
75
- ### Manual Native Setup
76
-
77
- If you're not using Expo, refer to the [Facebook SDK documentation](https://developers.facebook.com/docs/app-events/getting-started-app-events-android/) for manual iOS and Android configuration.
78
-
79
- ## Usage
80
-
81
- ### Log Events
39
+ ## Quick start
82
40
 
83
41
  ```typescript
84
- import { logEvent, logPurchase, AppEvents, AppEventParams } from 'expo-facebook-analytics';
42
+ import { logEvent, logPurchase, AppEvents } from 'expo-facebook-analytics';
85
43
 
86
- // Simple event
87
44
  logEvent(AppEvents.ViewedContent);
88
-
89
- // Event with a numeric value
90
- logEvent(AppEvents.Searched, 1);
91
-
92
- // Event with parameters
93
- logEvent(AppEvents.AddedToCart, {
94
- [AppEventParams.ContentID]: 'product-123',
95
- [AppEventParams.ContentType]: 'product',
96
- [AppEventParams.Currency]: 'USD',
97
- });
98
-
99
- // Event with value and parameters
100
- logEvent(AppEvents.Purchased, 29.99, {
101
- [AppEventParams.Currency]: 'USD',
102
- [AppEventParams.NumItems]: 2,
103
- });
104
-
105
- // Log a purchase
106
- logPurchase(29.99, 'USD', {
107
- [AppEventParams.ContentID]: 'product-123',
108
- });
109
-
110
- // Log push notification open
111
- logPushNotificationOpen({ campaign: 'summer-sale' });
112
- ```
113
-
114
- ### User Identity
115
-
116
- ```typescript
117
- import { setUserID, getUserID, clearUserID, setUserData } from 'expo-facebook-analytics';
118
-
119
- // Set user ID for analytics
120
- setUserID('user-123');
121
-
122
- // Get current user ID
123
- const userId = getUserID();
124
-
125
- // Clear user ID
126
- clearUserID();
127
-
128
- // Set user data for advanced matching
129
- setUserData({
130
- email: 'user@example.com',
131
- firstName: 'John',
132
- lastName: 'Doe',
133
- gender: 'm',
134
- city: 'San Francisco',
135
- state: 'CA',
136
- country: 'US',
137
- });
45
+ logEvent(AppEvents.AddedToCart, { product: 'sku-123' });
46
+ logPurchase(29.99, 'USD');
138
47
  ```
139
48
 
140
- ### Device Identifiers
49
+ For deferred (consent-gated) initialization, set `isAutoInitEnabled: false` in the
50
+ plugin and call `initialize()` yourself; with `autoLogAppEventsEnabled: false`, also
51
+ call `activateApp()` so Meta still receives the install event. Enable the Facebook
52
+ SDK's native logging with `setLoggingEnabled(true)`. See the [documentation](https://jasperaelvoet.github.io/expo-facebook-analytics/) for the full API.
141
53
 
142
- ```typescript
143
- import { getAnonymousID, getAdvertiserID } from 'expo-facebook-analytics';
144
-
145
- const anonId = await getAnonymousID();
146
- const advertiserId = await getAdvertiserID(); // IDFA on iOS, AAID on Android
147
- ```
54
+ See the **[documentation](https://jasperaelvoet.github.io/expo-facebook-analytics/)** for the full API,
55
+ config-plugin options, manual initialization, and predefined constants.
148
56
 
149
- ### Configuration
150
-
151
- ```typescript
152
- import { setFlushBehavior, flush, setPushNotificationsDeviceToken, setPushNotificationsRegistrationId } from 'expo-facebook-analytics';
153
-
154
- // Control event batching: 'auto' (default) or 'explicit_only'
155
- setFlushBehavior('explicit_only');
156
-
157
- // Manually flush queued events
158
- flush();
159
-
160
- // Push notification tokens
161
- setPushNotificationsDeviceToken(apnsToken); // iOS
162
- setPushNotificationsRegistrationId(fcmToken); // Android
163
- ```
164
-
165
- ## API Reference
166
-
167
- ### Event Logging
168
-
169
- | Function | Description |
170
- | --------------------------- | ------------------------------------ |
171
- | `logEvent(name, ...args)` | Log a custom or predefined event |
172
- | `logPurchase(amount, currency, params?)` | Log a purchase event |
173
- | `logPushNotificationOpen(payload?)` | Log a push notification open |
174
-
175
- ### User Identity
176
-
177
- | Function | Description |
178
- | ----------------------- | ---------------------------------- |
179
- | `setUserID(id)` | Set the user ID for analytics |
180
- | `clearUserID()` | Clear the current user ID |
181
- | `getUserID()` | Get the current user ID |
182
- | `setUserData(data)` | Set user data for advanced matching|
183
-
184
- ### Device Identifiers
185
-
186
- | Function | Description |
187
- | --------------------- | ------------------------------------------- |
188
- | `getAnonymousID()` | Get the anonymous device ID |
189
- | `getAdvertiserID()` | Get IDFA (iOS) or AAID (Android) |
190
-
191
- ### Configuration
192
-
193
- | Function | Description |
194
- | ------------------------------------- | ---------------------------------- |
195
- | `setFlushBehavior(behavior)` | Set event flush behavior |
196
- | `flush()` | Flush queued events immediately |
197
- | `setPushNotificationsDeviceToken(t)` | Set APNs push token (iOS) |
198
- | `setPushNotificationsRegistrationId(id)` | Set FCM registration ID (Android) |
199
-
200
- ## Predefined Constants
201
-
202
- ### AppEvents
203
-
204
- `AchievedLevel`, `AdClick`, `AdImpression`, `AddedPaymentInfo`, `AddedToCart`, `AddedToWishlist`, `CompletedRegistration`, `CompletedTutorial`, `Contact`, `CustomizeProduct`, `Donate`, `FindLocation`, `InitiatedCheckout`, `Purchased`, `Rated`, `Schedule`, `Searched`, `SpentCredits`, `StartTrial`, `SubmitApplication`, `Subscribe`, `UnlockedAchievement`, `ViewedContent`
57
+ ## Requirements
205
58
 
206
- ### AppEventParams
59
+ | Tool | Version |
60
+ | ---------------------------- | ------- |
61
+ | Expo SDK | 56+ |
62
+ | React Native | 0.85+ |
63
+ | `react-native-nitro-modules` | 0.35.9+ |
64
+ | iOS deployment target | 16.4+ |
65
+ | Android `minSdkVersion` | 24+ |
207
66
 
208
- `AddType`, `Content`, `ContentID`, `ContentType`, `Currency`, `Description`, `Level`, `MaxRatingValue`, `NumItems`, `OrderId`, `PaymentInfoAvailable`, `RegistrationMethod`, `SearchString`, `Success`, `ValueNo`, `ValueYes`
67
+ Native SDKs: Facebook iOS/Android SDK 18. Requires the React Native New Architecture (default in SDK 56).
209
68
 
210
- ## Types
69
+ ## Contributing
211
70
 
212
- ```typescript
213
- type Params = { [key: string]: string | number };
214
-
215
- type UserData = Readonly<{
216
- email?: string;
217
- firstName?: string;
218
- lastName?: string;
219
- phone?: string;
220
- dateOfBirth?: string;
221
- gender?: 'm' | 'f';
222
- city?: string;
223
- state?: string;
224
- zip?: string;
225
- country?: string;
226
- }>;
227
-
228
- type AppEventsFlushBehavior = 'auto' | 'explicit_only';
71
+ ```sh
72
+ bun install # install dependencies
73
+ bun run nitrogen # regenerate native bindings
74
+ bun run prepare # build the library
75
+ bun test # run tests
76
+ bun run check # lint & format
229
77
  ```
230
78
 
231
- ## Requirements
232
-
233
- | Platform | Minimum Version |
234
- | -------- | --------------- |
235
- | iOS | 13.0 |
236
- | Android | SDK 21 |
237
-
238
- Native SDKs: Facebook SDK ~17.0
79
+ To run the example app, see [`example/`](./example).
239
80
 
240
81
  ## License
241
82
 
@@ -0,0 +1,16 @@
1
+ project(NitroFBAnalytics)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set(CMAKE_VERBOSE_MAKEFILE ON)
5
+ set(CMAKE_CXX_STANDARD 20)
6
+
7
+ # The shared library loaded by NitroFBAnalyticsOnLoad.initializeNative()
8
+ # (System.loadLibrary("NitroFBAnalytics")). Its real sources are added by the
9
+ # generated autolinking cmake below.
10
+ add_library(NitroFBAnalytics SHARED
11
+ src/main/cpp/cpp-adapter.cpp
12
+ )
13
+
14
+ # Adds the Nitrogen-generated C++ sources, headers, and links (fbjni,
15
+ # ReactAndroid, react-native-nitro-modules) to the NitroFBAnalytics target.
16
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroFBAnalytics+autolinking.cmake)
@@ -7,44 +7,61 @@ buildscript {
7
7
  mavenCentral()
8
8
  }
9
9
  dependencies {
10
- classpath("com.android.tools.build:gradle:8.2.0")
11
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
10
+ classpath("com.android.tools.build:gradle:${safeExtGet('androidGradlePluginVersion', '8.9.2')}")
11
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '2.1.20')}")
12
12
  }
13
13
  }
14
14
 
15
15
  apply plugin: 'com.android.library'
16
16
  apply plugin: 'kotlin-android'
17
17
 
18
- def nitrogenAutolinking = file("nitrogen/generated/android/NitroFBAnalytics-autolinking.gradle")
18
+ def nitrogenAutolinking = file("../nitrogen/generated/android/NitroFBAnalytics+autolinking.gradle")
19
19
  if (nitrogenAutolinking.exists()) {
20
20
  apply from: nitrogenAutolinking
21
21
  }
22
22
 
23
23
  android {
24
24
  namespace 'com.nitrofbanalytics'
25
- compileSdkVersion safeExtGet('compileSdkVersion', 34)
25
+ compileSdkVersion safeExtGet('compileSdkVersion', 36)
26
26
 
27
27
  defaultConfig {
28
- minSdkVersion safeExtGet('minSdkVersion', 21)
29
- targetSdkVersion safeExtGet('targetSdkVersion', 34)
28
+ minSdkVersion safeExtGet('minSdkVersion', 24)
29
+ targetSdkVersion safeExtGet('targetSdkVersion', 36)
30
+
31
+ externalNativeBuild {
32
+ cmake {
33
+ cppFlags "-O2", "-frtti", "-fexceptions", "-Wall", "-std=c++20"
34
+ arguments "-DANDROID_STL=c++_shared"
35
+ }
36
+ }
37
+ }
38
+
39
+ buildFeatures {
40
+ prefab true
41
+ }
42
+
43
+ externalNativeBuild {
44
+ cmake {
45
+ path "CMakeLists.txt"
46
+ }
30
47
  }
31
48
 
32
49
  sourceSets {
33
50
  main {
34
51
  java.srcDirs += [
35
52
  'src/main/java',
36
- 'nitrogen/generated/android/kotlin'
53
+ '../nitrogen/generated/android/kotlin'
37
54
  ]
38
55
  }
39
56
  }
40
57
 
41
58
  compileOptions {
42
- sourceCompatibility JavaVersion.VERSION_1_8
43
- targetCompatibility JavaVersion.VERSION_1_8
59
+ sourceCompatibility JavaVersion.VERSION_17
60
+ targetCompatibility JavaVersion.VERSION_17
44
61
  }
45
62
 
46
63
  kotlinOptions {
47
- jvmTarget = '1.8'
64
+ jvmTarget = '17'
48
65
  }
49
66
  }
50
67
 
@@ -55,6 +72,14 @@ repositories {
55
72
 
56
73
  dependencies {
57
74
  implementation 'com.facebook.react:react-native:+'
58
- implementation 'com.margelo.nitro:nitro-modules:+'
75
+ // react-native-nitro-modules does not publish `com.margelo.nitro:nitro-modules`
76
+ // to a Maven repo and autolinking doesn't substitute it, so depend on the
77
+ // autolinked Gradle project directly. Fall back to the coordinate for
78
+ // standalone builds where the project isn't part of the Gradle build.
79
+ if (findProject(':react-native-nitro-modules') != null) {
80
+ implementation project(':react-native-nitro-modules')
81
+ } else {
82
+ implementation 'com.margelo.nitro:nitro-modules:+'
83
+ }
59
84
  implementation 'com.facebook.android:facebook-android-sdk:18.0.3'
60
85
  }
@@ -0,0 +1,14 @@
1
+ #include <jni.h>
2
+ #include <fbjni/fbjni.h>
3
+
4
+ #include "NitroFBAnalyticsOnLoad.hpp"
5
+
6
+ // Entry point of libNitroFBAnalytics.so. Loaded by
7
+ // NitroFBAnalyticsOnLoad.initializeNative() (System.loadLibrary), this
8
+ // registers the "FacebookAnalytics" HybridObject with the Nitro registry so
9
+ // JS `createHybridObject("FacebookAnalytics")` can resolve it.
10
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
11
+ return facebook::jni::initialize(vm, [] {
12
+ margelo::nitro::fbanalytics::registerAllNatives();
13
+ });
14
+ }
@@ -0,0 +1,30 @@
1
+ package com.nitrofbanalytics
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+ import com.margelo.nitro.com.nitrofbanalytics.NitroFBAnalyticsOnLoad
8
+
9
+ /**
10
+ * React Native autolinking entry point for the Nitro module.
11
+ *
12
+ * Unlike iOS (where Nitro auto-registers via the podspec's autolinking files),
13
+ * Android has no auto-load mechanism for the C++ library. This package exists
14
+ * solely so React Native autolinking instantiates it and triggers
15
+ * [NitroFBAnalyticsOnLoad.initializeNative], which loads the native library and
16
+ * registers the "FacebookAnalytics" HybridObject in the Nitro registry.
17
+ */
18
+ class FacebookAnalyticsPackage : ReactPackage {
19
+ init {
20
+ NitroFBAnalyticsOnLoad.initializeNative()
21
+ }
22
+
23
+ override fun createNativeModules(
24
+ reactContext: ReactApplicationContext,
25
+ ): List<NativeModule> = emptyList()
26
+
27
+ override fun createViewManagers(
28
+ reactContext: ReactApplicationContext,
29
+ ): List<ViewManager<*, *>> = emptyList()
30
+ }
@@ -1,11 +1,15 @@
1
- package com.nitrofbanalytics
1
+ package com.margelo.nitro.com.nitrofbanalytics
2
2
 
3
+ import android.app.Application
3
4
  import android.os.Bundle
4
5
  import com.facebook.FacebookSdk
6
+ import com.facebook.LoggingBehavior
5
7
  import com.facebook.appevents.AppEventsConstants
6
8
  import com.facebook.appevents.AppEventsLogger
7
9
  import com.facebook.internal.AttributionIdentifiers
8
10
  import com.margelo.nitro.core.Promise
11
+ import kotlinx.coroutines.CoroutineScope
12
+ import kotlinx.coroutines.Dispatchers
9
13
  import java.math.BigDecimal
10
14
  import java.util.Currency
11
15
 
@@ -15,8 +19,67 @@ class HybridFacebookAnalytics : HybridFacebookAnalyticsSpec() {
15
19
  AppEventsLogger.newLogger(FacebookSdk.getApplicationContext())
16
20
  }
17
21
 
22
+ // Background scope for blocking SDK calls (Play Services I/O) so they never
23
+ // run on the JS thread.
24
+ private val ioScope = CoroutineScope(Dispatchers.IO)
25
+
26
+ // region Initialization & Runtime Configuration
27
+
28
+ override fun initialize() {
29
+ FacebookSdk.setAutoInitEnabled(true)
30
+ FacebookSdk.fullyInitialize()
31
+ }
32
+
33
+ override fun setAutoInitEnabled(enabled: Boolean) {
34
+ FacebookSdk.setAutoInitEnabled(enabled)
35
+ }
36
+
37
+ override fun setAutoLogAppEventsEnabled(enabled: Boolean) {
38
+ FacebookSdk.setAutoLogAppEventsEnabled(enabled)
39
+ }
40
+
41
+ override fun setAdvertiserIDCollectionEnabled(enabled: Boolean) {
42
+ FacebookSdk.setAdvertiserIDCollectionEnabled(enabled)
43
+ }
44
+
45
+ override fun setAppID(appID: String) {
46
+ FacebookSdk.setApplicationId(appID)
47
+ }
48
+
49
+ override fun setClientToken(clientToken: String) {
50
+ FacebookSdk.setClientToken(clientToken)
51
+ }
52
+
53
+ override fun setLoggingEnabled(enabled: Boolean) {
54
+ val behaviors = listOf(
55
+ LoggingBehavior.APP_EVENTS,
56
+ LoggingBehavior.REQUESTS,
57
+ LoggingBehavior.DEVELOPER_ERRORS,
58
+ LoggingBehavior.INCLUDE_ACCESS_TOKENS
59
+ )
60
+ FacebookSdk.setIsDebugEnabled(enabled)
61
+ for (behavior in behaviors) {
62
+ if (enabled) {
63
+ FacebookSdk.addLoggingBehavior(behavior)
64
+ } else {
65
+ FacebookSdk.removeLoggingBehavior(behavior)
66
+ }
67
+ }
68
+ }
69
+
70
+ // endregion
71
+
18
72
  // region Event Logging
19
73
 
74
+ override fun activateApp() {
75
+ // Publishes the install (with the Play install referrer) once per device,
76
+ // then the launch event โ€” the only path that does so when automatic
77
+ // event logging is off.
78
+ val application = FacebookSdk.getApplicationContext() as? Application
79
+ ?: throw IllegalStateException("The Facebook SDK has no Application context; call initialize() first")
80
+ AppEventsLogger.activateApp(application)
81
+ }
82
+
20
83
  override fun logEvent(eventName: String, valueToSum: Double, params: Map<String, String>) {
21
84
  val bundle = mapToBundle(params)
22
85
  logger.logEvent(eventName, valueToSum, bundle)
@@ -83,16 +146,22 @@ class HybridFacebookAnalytics : HybridFacebookAnalyticsSpec() {
83
146
  // region Device Identifiers
84
147
 
85
148
  override fun getAnonymousID(): Promise<String?> {
86
- return Promise.resolved(logger.anonymousAppDeviceGUID)
149
+ return Promise.resolved(
150
+ AppEventsLogger.getAnonymousAppDeviceGUID(FacebookSdk.getApplicationContext())
151
+ )
87
152
  }
88
153
 
89
154
  override fun getAdvertiserID(): Promise<String?> {
90
- return try {
91
- val context = FacebookSdk.getApplicationContext()
92
- val identifiers = AttributionIdentifiers.getAttributionIdentifiers(context)
93
- Promise.resolved(identifiers?.androidAdvertiserId)
94
- } catch (e: Exception) {
95
- Promise.resolved(null)
155
+ // AttributionIdentifiers performs blocking Play Services I/O, so run it
156
+ // off the JS thread on the IO dispatcher.
157
+ return Promise.async(ioScope) {
158
+ try {
159
+ val context = FacebookSdk.getApplicationContext()
160
+ val identifiers = AttributionIdentifiers.getAttributionIdentifiers(context)
161
+ identifiers?.androidAdvertiserId
162
+ } catch (e: Exception) {
163
+ null
164
+ }
96
165
  }
97
166
  }
98
167
 
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
12
12
  s.license = package['license']
13
13
  s.author = 'expo-facebook-analytics contributors'
14
14
  s.source = { :git => '.', :tag => s.version.to_s }
15
- s.platforms = { :ios => '13.0' }
15
+ s.platforms = { :ios => '16.4' }
16
16
  s.module_name = 'NitroFBAnalytics'
17
17
 
18
18
  s.source_files = [
@@ -5,8 +5,58 @@ import AdSupport
5
5
 
6
6
  class HybridFacebookAnalytics: HybridFacebookAnalyticsSpec {
7
7
 
8
+ // MARK: - Initialization & Runtime Configuration
9
+
10
+ func initialize() throws {
11
+ // On iOS, auto-init is controlled by Info.plist (FacebookAutoInitEnabled).
12
+ // When disabled, this performs the deferred initialization.
13
+ ApplicationDelegate.shared.initializeSDK()
14
+ }
15
+
16
+ func setAutoInitEnabled(enabled: Bool) throws {
17
+ // The iOS SDK dropped auto-initialization in v9; `initialize()` is the
18
+ // only way it starts, so there is nothing to persist or undo here.
19
+ }
20
+
21
+ func setAutoLogAppEventsEnabled(enabled: Bool) throws {
22
+ Settings.shared.isAutoLogAppEventsEnabled = enabled
23
+ }
24
+
25
+ func setAdvertiserIDCollectionEnabled(enabled: Bool) throws {
26
+ Settings.shared.isAdvertiserIDCollectionEnabled = enabled
27
+ }
28
+
29
+ func setAppID(appID: String) throws {
30
+ Settings.shared.appID = appID
31
+ }
32
+
33
+ func setClientToken(clientToken: String) throws {
34
+ Settings.shared.clientToken = clientToken
35
+ }
36
+
37
+ func setLoggingEnabled(enabled: Bool) throws {
38
+ let behaviors: [LoggingBehavior] = [
39
+ .appEvents, .networkRequests, .developerErrors, .informational,
40
+ ]
41
+ for behavior in behaviors {
42
+ if enabled {
43
+ Settings.shared.enableLoggingBehavior(behavior)
44
+ } else {
45
+ Settings.shared.disableLoggingBehavior(behavior)
46
+ }
47
+ }
48
+ }
49
+
8
50
  // MARK: - Event Logging
9
51
 
52
+ func activateApp() throws {
53
+ // Publishes the install and launch events. The SDK insists on the main
54
+ // thread for this call; Nitro calls arrive on the JS thread.
55
+ DispatchQueue.main.async {
56
+ AppEvents.shared.activateApp()
57
+ }
58
+ }
59
+
10
60
  func logEvent(eventName: String, valueToSum: Double, params: [String: String]) throws {
11
61
  let fbParams: [AppEvents.ParameterName: Any] = params.reduce(into: [:]) { result, pair in
12
62
  result[AppEvents.ParameterName(pair.key)] = pair.value
@@ -115,16 +165,24 @@ class HybridFacebookAnalytics: HybridFacebookAnalyticsSpec {
115
165
  // MARK: - Push Notifications
116
166
 
117
167
  func setPushNotificationsDeviceToken(token: String) throws {
118
- // Token comes as a hex string โ€” convert back to raw Data
119
- let hex = token.replacingOccurrences(of: " ", with: "")
120
- var data = Data()
121
- var index = hex.startIndex
122
- while index < hex.endIndex {
123
- let nextIndex = hex.index(index, offsetBy: 2)
124
- if let byte = UInt8(hex[index..<nextIndex], radix: 16) {
125
- data.append(byte)
168
+ // Token comes as a hex string โ€” convert back to raw Data.
169
+ // Iterate over UTF-8 bytes and pack each pair of hex digits into one byte.
170
+ var data = Data(capacity: token.utf8.count / 2)
171
+ var highNibble: UInt8? = nil
172
+ for ascii in token.utf8 {
173
+ let nibble: UInt8
174
+ switch ascii {
175
+ case 0x30...0x39: nibble = ascii - 0x30 // '0'-'9'
176
+ case 0x41...0x46: nibble = ascii - 0x41 + 10 // 'A'-'F'
177
+ case 0x61...0x66: nibble = ascii - 0x61 + 10 // 'a'-'f'
178
+ default: continue // skip spaces / non-hex
179
+ }
180
+ if let high = highNibble {
181
+ data.append((high << 4) | nibble)
182
+ highNibble = nil
183
+ } else {
184
+ highNibble = nibble
126
185
  }
127
- index = nextIndex
128
186
  }
129
187
  AppEvents.shared.setPushNotificationsDeviceToken(data)
130
188
  }