expo-facebook-analytics 1.1.0 → 1.1.2

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/README.md CHANGED
@@ -47,8 +47,9 @@ logPurchase(29.99, 'USD');
47
47
  ```
48
48
 
49
49
  For deferred (consent-gated) initialization, set `isAutoInitEnabled: false` in the
50
- plugin and call `initialize()` yourself. Enable the Facebook SDK's native logging
51
- with `setLoggingEnabled(true)`. See the [documentation](https://jasperaelvoet.github.io/expo-facebook-analytics/) for the full API.
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.
52
53
 
53
54
  See the **[documentation](https://jasperaelvoet.github.io/expo-facebook-analytics/)** for the full API,
54
55
  config-plugin options, manual initialization, and predefined constants.
@@ -31,7 +31,10 @@ android {
31
31
  externalNativeBuild {
32
32
  cmake {
33
33
  cppFlags "-O2", "-frtti", "-fexceptions", "-Wall", "-std=c++20"
34
- arguments "-DANDROID_STL=c++_shared"
34
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
35
+ // Match React Native and Nitro, including Expo's single-ABI dev builds.
36
+ abiFilters(*((rootProject.findProperty('reactNativeArchitectures') ?:
37
+ 'armeabi-v7a,x86,x86_64,arm64-v8a').split(',')))
35
38
  }
36
39
  }
37
40
  }
@@ -1,5 +1,6 @@
1
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
5
6
  import com.facebook.LoggingBehavior
@@ -29,6 +30,10 @@ class HybridFacebookAnalytics : HybridFacebookAnalyticsSpec() {
29
30
  FacebookSdk.fullyInitialize()
30
31
  }
31
32
 
33
+ override fun setAutoInitEnabled(enabled: Boolean) {
34
+ FacebookSdk.setAutoInitEnabled(enabled)
35
+ }
36
+
32
37
  override fun setAutoLogAppEventsEnabled(enabled: Boolean) {
33
38
  FacebookSdk.setAutoLogAppEventsEnabled(enabled)
34
39
  }
@@ -66,6 +71,15 @@ class HybridFacebookAnalytics : HybridFacebookAnalyticsSpec() {
66
71
 
67
72
  // region Event Logging
68
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
+
69
83
  override fun logEvent(eventName: String, valueToSum: Double, params: Map<String, String>) {
70
84
  val bundle = mapToBundle(params)
71
85
  logger.logEvent(eventName, valueToSum, bundle)
@@ -13,6 +13,11 @@ class HybridFacebookAnalytics: HybridFacebookAnalyticsSpec {
13
13
  ApplicationDelegate.shared.initializeSDK()
14
14
  }
15
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
+
16
21
  func setAutoLogAppEventsEnabled(enabled: Bool) throws {
17
22
  Settings.shared.isAutoLogAppEventsEnabled = enabled
18
23
  }
@@ -44,6 +49,14 @@ class HybridFacebookAnalytics: HybridFacebookAnalyticsSpec {
44
49
 
45
50
  // MARK: - Event Logging
46
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
+
47
60
  func logEvent(eventName: String, valueToSum: Double, params: [String: String]) throws {
48
61
  let fbParams: [AppEvents.ParameterName: Any] = params.reduce(into: [:]) { result, pair in
49
62
  result[AppEvents.ParameterName(pair.key)] = pair.value
package/lib/index.d.ts CHANGED
@@ -9,6 +9,15 @@ export { AppEventParams, AppEvents } from "./types";
9
9
  * after optionally configuring the app ID / client token at runtime.
10
10
  */
11
11
  export declare function initialize(): void;
12
+ /**
13
+ * Turn the native SDK's auto-init flag on or off at runtime.
14
+ *
15
+ * `initialize()` switches it on, and Android persists the flag, so an app that
16
+ * gates the SDK behind consent should call `setAutoInitEnabled(false)` when
17
+ * consent is withdrawn — otherwise the next launch initializes the SDK before
18
+ * JavaScript runs. No-op on iOS, which has had no auto-init since SDK 9.
19
+ */
20
+ export declare function setAutoInitEnabled(enabled: boolean): void;
12
21
  /**
13
22
  * Enable or disable automatic logging of app events at runtime.
14
23
  */
@@ -40,6 +49,16 @@ export declare function setLoggingEnabled(enabled: boolean): void;
40
49
  * @param args - Optional valueToSum (number) and/or params object
41
50
  */
42
51
  export declare function logEvent(eventName: string, ...args: Array<number | Params>): void;
52
+ /**
53
+ * Log an app activation.
54
+ *
55
+ * Publishes the install event (carrying the Google Play install referrer on
56
+ * Android) the first time it runs on a device, then the launch event. The
57
+ * Facebook SDK only does this by itself when automatic event logging is on, so
58
+ * call it once per launch after {@link initialize} whenever
59
+ * `autoLogAppEventsEnabled` is off — without it Meta cannot attribute installs.
60
+ */
61
+ export declare function activateApp(): void;
43
62
  /**
44
63
  * Log a purchase event.
45
64
  */
package/lib/index.js CHANGED
@@ -2,12 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AppEvents = exports.AppEventParams = void 0;
4
4
  exports.initialize = initialize;
5
+ exports.setAutoInitEnabled = setAutoInitEnabled;
5
6
  exports.setAutoLogAppEventsEnabled = setAutoLogAppEventsEnabled;
6
7
  exports.setAdvertiserIDCollectionEnabled = setAdvertiserIDCollectionEnabled;
7
8
  exports.setAppID = setAppID;
8
9
  exports.setClientToken = setClientToken;
9
10
  exports.setLoggingEnabled = setLoggingEnabled;
10
11
  exports.logEvent = logEvent;
12
+ exports.activateApp = activateApp;
11
13
  exports.logPurchase = logPurchase;
12
14
  exports.logPushNotificationOpen = logPushNotificationOpen;
13
15
  exports.setUserID = setUserID;
@@ -56,6 +58,17 @@ function toStringParams(params) {
56
58
  function initialize() {
57
59
  NativeFBAnalytics.initialize();
58
60
  }
61
+ /**
62
+ * Turn the native SDK's auto-init flag on or off at runtime.
63
+ *
64
+ * `initialize()` switches it on, and Android persists the flag, so an app that
65
+ * gates the SDK behind consent should call `setAutoInitEnabled(false)` when
66
+ * consent is withdrawn — otherwise the next launch initializes the SDK before
67
+ * JavaScript runs. No-op on iOS, which has had no auto-init since SDK 9.
68
+ */
69
+ function setAutoInitEnabled(enabled) {
70
+ NativeFBAnalytics.setAutoInitEnabled(enabled);
71
+ }
59
72
  /**
60
73
  * Enable or disable automatic logging of app events at runtime.
61
74
  */
@@ -115,6 +128,18 @@ function logEvent(eventName, ...args) {
115
128
  NativeFBAnalytics.logEventWithoutParams(eventName, valueToSum);
116
129
  }
117
130
  }
131
+ /**
132
+ * Log an app activation.
133
+ *
134
+ * Publishes the install event (carrying the Google Play install referrer on
135
+ * Android) the first time it runs on a device, then the launch event. The
136
+ * Facebook SDK only does this by itself when automatic event logging is on, so
137
+ * call it once per launch after {@link initialize} whenever
138
+ * `autoLogAppEventsEnabled` is off — without it Meta cannot attribute installs.
139
+ */
140
+ function activateApp() {
141
+ NativeFBAnalytics.activateApp();
142
+ }
118
143
  /**
119
144
  * Log a purchase event.
120
145
  */
@@ -4,6 +4,12 @@ export interface FacebookAnalytics extends HybridObject<{
4
4
  android: "kotlin";
5
5
  }> {
6
6
  initialize(): void;
7
+ /**
8
+ * Turn the native SDK's auto-init flag on or off at runtime. Android
9
+ * persists it, so an app that gates the SDK behind consent should switch it
10
+ * off again when consent is withdrawn. No-op on iOS, which has no auto-init.
11
+ */
12
+ setAutoInitEnabled(enabled: boolean): void;
7
13
  setAutoLogAppEventsEnabled(enabled: boolean): void;
8
14
  setAdvertiserIDCollectionEnabled(enabled: boolean): void;
9
15
  setAppID(appID: string): void;
@@ -13,6 +19,12 @@ export interface FacebookAnalytics extends HybridObject<{
13
19
  * developer errors). Logs are printed to the native console.
14
20
  */
15
21
  setLoggingEnabled(enabled: boolean): void;
22
+ /**
23
+ * Log an app activation: publishes the install (with the Play install
24
+ * referrer on Android) once per device, then the launch event. The only path
25
+ * that does so when automatic event logging is off.
26
+ */
27
+ activateApp(): void;
16
28
  logEvent(eventName: string, valueToSum: number, params: Record<string, string>): void;
17
29
  logEventWithoutParams(eventName: string, valueToSum: number): void;
18
30
  logPurchase(amount: number, currency: string, params: Record<string, string>): void;
@@ -52,6 +52,10 @@ namespace margelo::nitro::fbanalytics {
52
52
  static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("initialize");
53
53
  method(_javaPart);
54
54
  }
55
+ void JHybridFacebookAnalyticsSpec::setAutoInitEnabled(bool enabled) {
56
+ static const auto method = _javaPart->javaClassStatic()->getMethod<void(jboolean /* enabled */)>("setAutoInitEnabled");
57
+ method(_javaPart, enabled);
58
+ }
55
59
  void JHybridFacebookAnalyticsSpec::setAutoLogAppEventsEnabled(bool enabled) {
56
60
  static const auto method = _javaPart->javaClassStatic()->getMethod<void(jboolean /* enabled */)>("setAutoLogAppEventsEnabled");
57
61
  method(_javaPart, enabled);
@@ -72,6 +76,10 @@ namespace margelo::nitro::fbanalytics {
72
76
  static const auto method = _javaPart->javaClassStatic()->getMethod<void(jboolean /* enabled */)>("setLoggingEnabled");
73
77
  method(_javaPart, enabled);
74
78
  }
79
+ void JHybridFacebookAnalyticsSpec::activateApp() {
80
+ static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("activateApp");
81
+ method(_javaPart);
82
+ }
75
83
  void JHybridFacebookAnalyticsSpec::logEvent(const std::string& eventName, double valueToSum, const std::unordered_map<std::string, std::string>& params) {
76
84
  static const auto method = _javaPart->javaClassStatic()->getMethod<void(jni::alias_ref<jni::JString> /* eventName */, double /* valueToSum */, jni::alias_ref<jni::JMap<jni::JString, jni::JString>> /* params */)>("logEvent");
77
85
  method(_javaPart, jni::make_jstring(eventName), valueToSum, [&]() -> jni::local_ref<jni::JMap<jni::JString, jni::JString>> {
@@ -55,11 +55,13 @@ namespace margelo::nitro::fbanalytics {
55
55
  public:
56
56
  // Methods
57
57
  void initialize() override;
58
+ void setAutoInitEnabled(bool enabled) override;
58
59
  void setAutoLogAppEventsEnabled(bool enabled) override;
59
60
  void setAdvertiserIDCollectionEnabled(bool enabled) override;
60
61
  void setAppID(const std::string& appID) override;
61
62
  void setClientToken(const std::string& clientToken) override;
62
63
  void setLoggingEnabled(bool enabled) override;
64
+ void activateApp() override;
63
65
  void logEvent(const std::string& eventName, double valueToSum, const std::unordered_map<std::string, std::string>& params) override;
64
66
  void logEventWithoutParams(const std::string& eventName, double valueToSum) override;
65
67
  void logPurchase(double amount, const std::string& currency, const std::unordered_map<std::string, std::string>& params) override;
@@ -33,6 +33,10 @@ abstract class HybridFacebookAnalyticsSpec: HybridObject() {
33
33
  @Keep
34
34
  abstract fun initialize(): Unit
35
35
 
36
+ @DoNotStrip
37
+ @Keep
38
+ abstract fun setAutoInitEnabled(enabled: Boolean): Unit
39
+
36
40
  @DoNotStrip
37
41
  @Keep
38
42
  abstract fun setAutoLogAppEventsEnabled(enabled: Boolean): Unit
@@ -53,6 +57,10 @@ abstract class HybridFacebookAnalyticsSpec: HybridObject() {
53
57
  @Keep
54
58
  abstract fun setLoggingEnabled(enabled: Boolean): Unit
55
59
 
60
+ @DoNotStrip
61
+ @Keep
62
+ abstract fun activateApp(): Unit
63
+
56
64
  @DoNotStrip
57
65
  @Keep
58
66
  abstract fun logEvent(eventName: String, valueToSum: Double, params: Map<String, String>): Unit
@@ -75,6 +75,12 @@ namespace margelo::nitro::fbanalytics {
75
75
  std::rethrow_exception(__result.error());
76
76
  }
77
77
  }
78
+ inline void setAutoInitEnabled(bool enabled) override {
79
+ auto __result = _swiftPart.setAutoInitEnabled(std::forward<decltype(enabled)>(enabled));
80
+ if (__result.hasError()) [[unlikely]] {
81
+ std::rethrow_exception(__result.error());
82
+ }
83
+ }
78
84
  inline void setAutoLogAppEventsEnabled(bool enabled) override {
79
85
  auto __result = _swiftPart.setAutoLogAppEventsEnabled(std::forward<decltype(enabled)>(enabled));
80
86
  if (__result.hasError()) [[unlikely]] {
@@ -105,6 +111,12 @@ namespace margelo::nitro::fbanalytics {
105
111
  std::rethrow_exception(__result.error());
106
112
  }
107
113
  }
114
+ inline void activateApp() override {
115
+ auto __result = _swiftPart.activateApp();
116
+ if (__result.hasError()) [[unlikely]] {
117
+ std::rethrow_exception(__result.error());
118
+ }
119
+ }
108
120
  inline void logEvent(const std::string& eventName, double valueToSum, const std::unordered_map<std::string, std::string>& params) override {
109
121
  auto __result = _swiftPart.logEvent(eventName, std::forward<decltype(valueToSum)>(valueToSum), params);
110
122
  if (__result.hasError()) [[unlikely]] {
@@ -14,11 +14,13 @@ public protocol HybridFacebookAnalyticsSpec_protocol: HybridObject {
14
14
 
15
15
  // Methods
16
16
  func initialize() throws -> Void
17
+ func setAutoInitEnabled(enabled: Bool) throws -> Void
17
18
  func setAutoLogAppEventsEnabled(enabled: Bool) throws -> Void
18
19
  func setAdvertiserIDCollectionEnabled(enabled: Bool) throws -> Void
19
20
  func setAppID(appID: String) throws -> Void
20
21
  func setClientToken(clientToken: String) throws -> Void
21
22
  func setLoggingEnabled(enabled: Bool) throws -> Void
23
+ func activateApp() throws -> Void
22
24
  func logEvent(eventName: String, valueToSum: Double, params: Dictionary<String, String>) throws -> Void
23
25
  func logEventWithoutParams(eventName: String, valueToSum: Double) throws -> Void
24
26
  func logPurchase(amount: Double, currency: String, params: Dictionary<String, String>) throws -> Void
@@ -135,6 +135,17 @@ open class HybridFacebookAnalyticsSpec_cxx {
135
135
  }
136
136
  }
137
137
 
138
+ @inline(__always)
139
+ public final func setAutoInitEnabled(enabled: Bool) -> bridge.Result_void_ {
140
+ do {
141
+ try self.__implementation.setAutoInitEnabled(enabled: enabled)
142
+ return bridge.create_Result_void_()
143
+ } catch (let __error) {
144
+ let __exceptionPtr = __error.toCpp()
145
+ return bridge.create_Result_void_(__exceptionPtr)
146
+ }
147
+ }
148
+
138
149
  @inline(__always)
139
150
  public final func setAutoLogAppEventsEnabled(enabled: Bool) -> bridge.Result_void_ {
140
151
  do {
@@ -190,6 +201,17 @@ open class HybridFacebookAnalyticsSpec_cxx {
190
201
  }
191
202
  }
192
203
 
204
+ @inline(__always)
205
+ public final func activateApp() -> bridge.Result_void_ {
206
+ do {
207
+ try self.__implementation.activateApp()
208
+ return bridge.create_Result_void_()
209
+ } catch (let __error) {
210
+ let __exceptionPtr = __error.toCpp()
211
+ return bridge.create_Result_void_(__exceptionPtr)
212
+ }
213
+ }
214
+
193
215
  @inline(__always)
194
216
  public final func logEvent(eventName: std.string, valueToSum: Double, params: bridge.std__unordered_map_std__string__std__string_) -> bridge.Result_void_ {
195
217
  do {
@@ -15,11 +15,13 @@ namespace margelo::nitro::fbanalytics {
15
15
  // load custom methods/properties
16
16
  registerHybrids(this, [](Prototype& prototype) {
17
17
  prototype.registerHybridMethod("initialize", &HybridFacebookAnalyticsSpec::initialize);
18
+ prototype.registerHybridMethod("setAutoInitEnabled", &HybridFacebookAnalyticsSpec::setAutoInitEnabled);
18
19
  prototype.registerHybridMethod("setAutoLogAppEventsEnabled", &HybridFacebookAnalyticsSpec::setAutoLogAppEventsEnabled);
19
20
  prototype.registerHybridMethod("setAdvertiserIDCollectionEnabled", &HybridFacebookAnalyticsSpec::setAdvertiserIDCollectionEnabled);
20
21
  prototype.registerHybridMethod("setAppID", &HybridFacebookAnalyticsSpec::setAppID);
21
22
  prototype.registerHybridMethod("setClientToken", &HybridFacebookAnalyticsSpec::setClientToken);
22
23
  prototype.registerHybridMethod("setLoggingEnabled", &HybridFacebookAnalyticsSpec::setLoggingEnabled);
24
+ prototype.registerHybridMethod("activateApp", &HybridFacebookAnalyticsSpec::activateApp);
23
25
  prototype.registerHybridMethod("logEvent", &HybridFacebookAnalyticsSpec::logEvent);
24
26
  prototype.registerHybridMethod("logEventWithoutParams", &HybridFacebookAnalyticsSpec::logEventWithoutParams);
25
27
  prototype.registerHybridMethod("logPurchase", &HybridFacebookAnalyticsSpec::logPurchase);
@@ -52,11 +52,13 @@ namespace margelo::nitro::fbanalytics {
52
52
  public:
53
53
  // Methods
54
54
  virtual void initialize() = 0;
55
+ virtual void setAutoInitEnabled(bool enabled) = 0;
55
56
  virtual void setAutoLogAppEventsEnabled(bool enabled) = 0;
56
57
  virtual void setAdvertiserIDCollectionEnabled(bool enabled) = 0;
57
58
  virtual void setAppID(const std::string& appID) = 0;
58
59
  virtual void setClientToken(const std::string& clientToken) = 0;
59
60
  virtual void setLoggingEnabled(bool enabled) = 0;
61
+ virtual void activateApp() = 0;
60
62
  virtual void logEvent(const std::string& eventName, double valueToSum, const std::unordered_map<std::string, std::string>& params) = 0;
61
63
  virtual void logEventWithoutParams(const std::string& eventName, double valueToSum) = 0;
62
64
  virtual void logPurchase(double amount, const std::string& currency, const std::unordered_map<std::string, std::string>& params) = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-facebook-analytics",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "High-performance Facebook Analytics for React Native using Nitro Modules",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -11,6 +11,15 @@ type ResolvedProps = {
11
11
  };
12
12
  export declare const withFacebookIOS: ConfigPlugin<ResolvedProps>;
13
13
  export declare const withUserTrackingPermission: ConfigPlugin<ResolvedProps>;
14
+ /**
15
+ * Wire the Facebook SDK into the app delegate so it initializes at launch.
16
+ *
17
+ * Expo SDK 53 and newer generate `AppDelegate.swift` (an `ExpoAppDelegate`
18
+ * subclass); older templates are Objective-C. Both are handled, and the patch
19
+ * is idempotent. Exported without the config-plugin wrapper so it can be
20
+ * unit-tested against template snippets.
21
+ */
22
+ export declare function applyFacebookAppDelegate(contents: string, language: string): string;
14
23
  export declare const withFacebookAppDelegate: ConfigPlugin<ResolvedProps>;
15
24
  export declare const withSKAdNetworkIdentifiers: ConfigPlugin<string[]>;
16
25
  export {};
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.withSKAdNetworkIdentifiers = exports.withFacebookAppDelegate = exports.withUserTrackingPermission = exports.withFacebookIOS = void 0;
4
+ exports.applyFacebookAppDelegate = applyFacebookAppDelegate;
4
5
  const config_plugins_1 = require("@expo/config-plugins");
5
6
  const withFacebookIOS = (config, props) => {
6
7
  return (0, config_plugins_1.withInfoPlist)(config, (mod) => {
@@ -55,26 +56,64 @@ const withUserTrackingPermission = (config, props) => {
55
56
  });
56
57
  };
57
58
  exports.withUserTrackingPermission = withUserTrackingPermission;
58
- const withFacebookAppDelegate = (config, _props) => {
59
- return (0, config_plugins_1.withAppDelegate)(config, (mod) => {
60
- const contents = mod.modResults.contents;
61
- // Add import
62
- if (!contents.includes("#import <FBSDKCoreKit/FBSDKCoreKit.h>")) {
63
- mod.modResults.contents = mod.modResults.contents.replace('#import "AppDelegate.h"', '#import "AppDelegate.h"\n#import <FBSDKCoreKit/FBSDKCoreKit.h>');
59
+ const OBJC_IMPORT = "#import <FBSDKCoreKit/FBSDKCoreKit.h>";
60
+ const OBJC_LAUNCH_HOOK = "[[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];";
61
+ const SWIFT_IMPORT = "import FBSDKCoreKit";
62
+ const SWIFT_LAUNCH_HOOK = "ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)";
63
+ const SWIFT_LAUNCH_ANCHOR = /^([ \t]*)return super\.application\(application, didFinishLaunchingWithOptions: launchOptions\)/m;
64
+ /**
65
+ * Wire the Facebook SDK into the app delegate so it initializes at launch.
66
+ *
67
+ * Expo SDK 53 and newer generate `AppDelegate.swift` (an `ExpoAppDelegate`
68
+ * subclass); older templates are Objective-C. Both are handled, and the patch
69
+ * is idempotent. Exported without the config-plugin wrapper so it can be
70
+ * unit-tested against template snippets.
71
+ */
72
+ function applyFacebookAppDelegate(contents, language) {
73
+ let result = contents;
74
+ if (language === "swift") {
75
+ if (!result.includes(SWIFT_IMPORT)) {
76
+ // Ahead of the first import, whatever access modifier the template uses
77
+ // (`internal import Expo` on SDK 57, plain `import Expo` before).
78
+ const withImport = result.replace(/^((?:\w+ )?import [^\n]+\n)/m, `${SWIFT_IMPORT}\n$1`);
79
+ result =
80
+ withImport === result ? `${SWIFT_IMPORT}\n${result}` : withImport;
64
81
  }
65
- // Add didFinishLaunchingWithOptions hook
66
- if (!contents.includes("FBSDKApplicationDelegate")) {
67
- mod.modResults.contents = mod.modResults.contents.replace("self.initialProps = @{};", "self.initialProps = @{};\n [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];");
82
+ if (!result.includes(SWIFT_LAUNCH_HOOK)) {
83
+ if (!SWIFT_LAUNCH_ANCHOR.test(result)) {
84
+ throw new Error("expo-facebook-analytics: could not find `return super.application(application, didFinishLaunchingWithOptions: launchOptions)` in AppDelegate.swift. " +
85
+ "Restore Expo's template delegate, or set `isAutoInitEnabled: false` and call `initialize()` from JavaScript.");
86
+ }
87
+ result = result.replace(SWIFT_LAUNCH_ANCHOR, `$1${SWIFT_LAUNCH_HOOK}\n$1return super.application(application, didFinishLaunchingWithOptions: launchOptions)`);
68
88
  }
69
- // Add openURL handler
70
- if (!contents.includes("openURL:url options:options")) {
71
- const openURLMethod = `
89
+ return result;
90
+ }
91
+ if (!result.includes(OBJC_IMPORT)) {
92
+ result = result.replace('#import "AppDelegate.h"', `#import "AppDelegate.h"\n${OBJC_IMPORT}`);
93
+ }
94
+ if (!result.includes("FBSDKApplicationDelegate")) {
95
+ result = result.replace("self.initialProps = @{};", `self.initialProps = @{};\n ${OBJC_LAUNCH_HOOK}`);
96
+ }
97
+ if (!result.includes("openURL:url options:options")) {
98
+ const openURLMethod = `
72
99
 
73
100
  - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
74
101
  return [[FBSDKApplicationDelegate sharedInstance] application:application openURL:url options:options];
75
102
  }`;
76
- mod.modResults.contents = mod.modResults.contents.replace(/@end\s*$/, `${openURLMethod}\n\n@end\n`);
77
- }
103
+ result = result.replace(/@end\s*$/, `${openURLMethod}\n\n@end\n`);
104
+ }
105
+ return result;
106
+ }
107
+ const withFacebookAppDelegate = (config, props) => {
108
+ // With auto-init off the app starts the SDK from JavaScript through
109
+ // `initialize()`; wiring the delegate anyway would start it at launch and
110
+ // defeat consent gating. (The iOS SDK has had no auto-init of its own since
111
+ // v9, so this wiring is the only launch-time path.)
112
+ if (!props.isAutoInitEnabled) {
113
+ return config;
114
+ }
115
+ return (0, config_plugins_1.withAppDelegate)(config, (mod) => {
116
+ mod.modResults.contents = applyFacebookAppDelegate(mod.modResults.contents, mod.modResults.language);
78
117
  return mod;
79
118
  });
80
119
  };
package/src/index.ts CHANGED
@@ -44,6 +44,18 @@ export function initialize(): void {
44
44
  NativeFBAnalytics.initialize();
45
45
  }
46
46
 
47
+ /**
48
+ * Turn the native SDK's auto-init flag on or off at runtime.
49
+ *
50
+ * `initialize()` switches it on, and Android persists the flag, so an app that
51
+ * gates the SDK behind consent should call `setAutoInitEnabled(false)` when
52
+ * consent is withdrawn — otherwise the next launch initializes the SDK before
53
+ * JavaScript runs. No-op on iOS, which has had no auto-init since SDK 9.
54
+ */
55
+ export function setAutoInitEnabled(enabled: boolean): void {
56
+ NativeFBAnalytics.setAutoInitEnabled(enabled);
57
+ }
58
+
47
59
  /**
48
60
  * Enable or disable automatic logging of app events at runtime.
49
61
  */
@@ -113,6 +125,19 @@ export function logEvent(
113
125
  }
114
126
  }
115
127
 
128
+ /**
129
+ * Log an app activation.
130
+ *
131
+ * Publishes the install event (carrying the Google Play install referrer on
132
+ * Android) the first time it runs on a device, then the launch event. The
133
+ * Facebook SDK only does this by itself when automatic event logging is on, so
134
+ * call it once per launch after {@link initialize} whenever
135
+ * `autoLogAppEventsEnabled` is off — without it Meta cannot attribute installs.
136
+ */
137
+ export function activateApp(): void {
138
+ NativeFBAnalytics.activateApp();
139
+ }
140
+
116
141
  /**
117
142
  * Log a purchase event.
118
143
  */
@@ -4,6 +4,12 @@ export interface FacebookAnalytics
4
4
  extends HybridObject<{ ios: "swift"; android: "kotlin" }> {
5
5
  // SDK initialization & runtime configuration
6
6
  initialize(): void;
7
+ /**
8
+ * Turn the native SDK's auto-init flag on or off at runtime. Android
9
+ * persists it, so an app that gates the SDK behind consent should switch it
10
+ * off again when consent is withdrawn. No-op on iOS, which has no auto-init.
11
+ */
12
+ setAutoInitEnabled(enabled: boolean): void;
7
13
  setAutoLogAppEventsEnabled(enabled: boolean): void;
8
14
  setAdvertiserIDCollectionEnabled(enabled: boolean): void;
9
15
  setAppID(appID: string): void;
@@ -15,6 +21,12 @@ export interface FacebookAnalytics
15
21
  setLoggingEnabled(enabled: boolean): void;
16
22
 
17
23
  // Event logging
24
+ /**
25
+ * Log an app activation: publishes the install (with the Play install
26
+ * referrer on Android) once per device, then the launch event. The only path
27
+ * that does so when automatic event logging is off.
28
+ */
29
+ activateApp(): void;
18
30
  logEvent(
19
31
  eventName: string,
20
32
  valueToSum: number,