@amplytools/react-native-amply-sdk 0.1.0 → 0.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.
@@ -0,0 +1,177 @@
1
+ # Development Guide
2
+
3
+ This guide is for SDK developers contributing to the Amply React Native SDK.
4
+
5
+ ## Project Structure
6
+
7
+ ```
8
+ src/ # TypeScript source (JS side)
9
+ ├── nativeSpecs/
10
+ │ └── NativeAmplyModule.ts # TurboModule spec (codegen source)
11
+ ├── index.ts # Public API exports
12
+ ├── nativeModule.ts # Native module loader with fallbacks
13
+ └── systemEvents.ts # Event emitter helpers
14
+
15
+ android/ # Android native code
16
+ ├── src/main/
17
+ │ ├── java/tools/amply/sdk/reactnative/
18
+ │ │ ├── AmplyModule.kt # TurboModule implementation
19
+ │ │ ├── AmplyPackage.kt # Package for React Native
20
+ │ │ └── core/ # Amply SDK wrapper
21
+ │ └── jni/
22
+ │ └── CMakeLists.txt # C++ build config
23
+ └── src/newarch/ # Auto-generated codegen artifacts
24
+
25
+ ios/ # iOS native code
26
+ ├── AmplyReactNative.podspec # CocoaPods spec
27
+ └── Sources/AmplyReactNative/
28
+ ├── AmplyModule.mm # TurboModule implementation
29
+ └── AmplyReactNative/ # Codegen artifacts (committed)
30
+ ├── AmplyReactNative.h
31
+ └── AmplyReactNative-generated.mm
32
+
33
+ example/bare/ # Bare RN example app
34
+ example/expo/ # Expo example app
35
+
36
+ plugin/ # Expo config plugin source
37
+ ├── src/
38
+ │ ├── withAmply.ts # Main plugin logic
39
+ │ └── index.ts # Plugin export
40
+ └── build/ # Compiled plugin (generated)
41
+ ```
42
+
43
+ ## Build Commands
44
+
45
+ ```bash
46
+ # Build TypeScript distribution
47
+ yarn build
48
+
49
+ # Build Expo config plugin
50
+ yarn build:plugin
51
+
52
+ # Run tests
53
+ yarn test
54
+
55
+ # Lint and type check
56
+ yarn lint && yarn typecheck
57
+ ```
58
+
59
+ ## Codegen
60
+
61
+ This SDK uses React Native TurboModules. When you modify the TypeScript spec (`src/nativeSpecs/NativeAmplyModule.ts`), the native codegen artifacts must be updated.
62
+
63
+ **Android:** Codegen runs automatically during Gradle build. No manual steps needed.
64
+
65
+ **iOS:** The committed codegen files must be manually updated:
66
+ - `ios/Sources/AmplyReactNative/AmplyReactNative/AmplyReactNative.h`
67
+ - `ios/Sources/AmplyReactNative/AmplyReactNative/AmplyReactNative-generated.mm`
68
+
69
+ When adding new methods or config fields to the spec:
70
+ 1. Add the method/field declaration to the header struct
71
+ 2. Add the inline implementation in the header
72
+ 3. Add the host function and method mapping in the generated .mm file
73
+ 4. Run `expo prebuild --clean` in example app to verify
74
+
75
+ ## Sample Apps
76
+
77
+ The SDK includes two example apps for testing during development.
78
+
79
+ ### Expo Sample (`example/expo`)
80
+
81
+ Recommended for fast iteration with hot reload.
82
+
83
+ ```bash
84
+ cd example/expo
85
+ yarn install
86
+ expo prebuild --clean
87
+ expo start # Terminal 1
88
+ expo run:android # Terminal 2 (Android)
89
+ expo run:ios # Terminal 2 (iOS)
90
+ ```
91
+
92
+ ### Bare React Native Sample (`example/bare`)
93
+
94
+ For production-like testing and intent filter validation.
95
+
96
+ ```bash
97
+ cd example/bare
98
+ yarn install
99
+ yarn react-native run-android # Android
100
+ yarn react-native run-ios # iOS
101
+ ```
102
+
103
+ ### Local SDK Development
104
+
105
+ Sample apps use `link:` protocol for local SDK development:
106
+
107
+ ```json
108
+ {
109
+ "dependencies": {
110
+ "@amplytools/react-native-amply-sdk": "link:../.."
111
+ }
112
+ }
113
+ ```
114
+
115
+ This allows immediate testing of SDK changes without npm reinstall.
116
+
117
+ > **Note:** Regular app developers should install from npm: `yarn add @amplytools/react-native-amply-sdk`
118
+
119
+ ## Development Workflow
120
+
121
+ ### 1. Setup workspace
122
+
123
+ ```bash
124
+ yarn install
125
+ ```
126
+
127
+ ### 2. Make changes
128
+
129
+ ```bash
130
+ # For TypeScript spec changes (new methods, config fields)
131
+ # 1. Edit the spec
132
+ nano src/nativeSpecs/NativeAmplyModule.ts
133
+ # 2. Update iOS codegen files manually (see Codegen section above)
134
+ # 3. Android codegen updates automatically during build
135
+
136
+ # For native implementation changes
137
+ nano android/src/main/java/tools/amply/sdk/reactnative/AmplyModule.kt
138
+ nano ios/Sources/AmplyReactNative/AmplyModule.mm
139
+
140
+ # For JS changes
141
+ nano src/index.ts
142
+ ```
143
+
144
+ ### 3. Test with sample apps
145
+
146
+ ```bash
147
+ # Test with Expo (faster feedback)
148
+ cd example/expo
149
+ rm -rf android ios
150
+ expo prebuild --clean
151
+ expo start # Terminal 1
152
+ expo run:android # Terminal 2
153
+
154
+ # Validate with bare RN
155
+ cd ../bare
156
+ yarn react-native run-android
157
+ ```
158
+
159
+ ## Testing Deep Links
160
+
161
+ ```bash
162
+ # iOS Simulator
163
+ xcrun simctl openurl booted "amply://campaign/promo/123"
164
+
165
+ # Android
166
+ adb shell am start -a android.intent.action.VIEW \
167
+ -d "amply://campaign/promo/123" <package>
168
+ ```
169
+
170
+ ## PR Checklist
171
+
172
+ - [ ] Changes tested in both example apps
173
+ - [ ] `yarn lint && yarn typecheck && yarn test` passes
174
+ - [ ] iOS codegen files updated (if spec changed)
175
+ - [ ] Commit messages are clear
176
+ - [ ] No breaking changes (or documented)
177
+ - [ ] README updated if API changes
@@ -0,0 +1,326 @@
1
+ # Debug Feature Implementation Summary
2
+
3
+ ## Overview
4
+
5
+ This document summarizes the implementation of the debug/logging feature for the Amply React Native SDK, as requested in `AMPLY_SDK_DEBUG_FEATURE_REQUEST.md`.
6
+
7
+ ## Architecture Decision
8
+
9
+ **Chosen Approach**: Centralized logging in KMP SDK with event-based forwarding to React Native.
10
+
11
+ Instead of implementing logging separately in Android/iOS React Native wrappers, all logging originates from the KMP SDK and is forwarded to JS via the existing system events mechanism. This approach:
12
+ - Centralizes logging logic in one place
13
+ - Enables campaign evaluation logging (only possible in KMP SDK)
14
+ - Reduces code duplication across platforms
15
+
16
+ ---
17
+
18
+ ## Files Changed
19
+
20
+ ### 1. KMP SDK (multiplatform-library-template)
21
+
22
+ #### New File: `library/src/commonMain/kotlin/tools/amply/sdk/logging/Logger.kt`
23
+
24
+ Created a complete logging infrastructure:
25
+
26
+ ```kotlin
27
+ // Log levels
28
+ enum class LogLevel(val level: Int) {
29
+ NONE(0), ERROR(1), WARN(2), INFO(3), DEBUG(4)
30
+ }
31
+
32
+ // Log categories for filtering
33
+ object LogCategory {
34
+ const val SDK = "sdk"
35
+ const val EVENT = "event"
36
+ const val CAMPAIGN = "campaign"
37
+ const val CONFIG = "config"
38
+ const val SESSION = "session"
39
+ const val DEEPLINK = "deeplink"
40
+ const val NETWORK = "network"
41
+ }
42
+
43
+ // Log entry data class
44
+ data class LogEntry(
45
+ val level: LogLevel,
46
+ val category: String,
47
+ val message: String,
48
+ val timestamp: Long,
49
+ val details: Map<String, Any>? = null
50
+ )
51
+
52
+ // Listener interface for log events
53
+ interface LogListener {
54
+ fun onLog(entry: LogEntry)
55
+ }
56
+
57
+ // Singleton logger
58
+ object Logger {
59
+ var currentLevel: LogLevel = LogLevel.NONE
60
+ private var listener: LogListener? = null
61
+
62
+ fun setLevel(level: LogLevel)
63
+ fun setListener(listener: LogListener?)
64
+ fun log(level: LogLevel, category: String, message: String, details: Map<String, Any>? = null)
65
+
66
+ // Convenience methods
67
+ fun debug(category: String, message: String, ...)
68
+ fun info(category: String, message: String, ...)
69
+ fun warn(category: String, message: String, ...)
70
+ fun error(category: String, message: String, ...)
71
+ }
72
+ ```
73
+
74
+ #### Modified: `library/src/commonMain/kotlin/tools/amply/sdk/campaigns/CampaignManager.kt`
75
+
76
+ Added campaign evaluation logging:
77
+
78
+ ```kotlin
79
+ // In fetchCampaigns()
80
+ Logger.info(LogCategory.CONFIG, "Config fetch started")
81
+ Logger.info(LogCategory.CONFIG, "Config fetch completed: ${campaigns.size} campaigns loaded")
82
+
83
+ // In activate()
84
+ Logger.debug(LogCategory.CAMPAIGN, "Evaluating ${campaigns.size} campaigns for event: ${event.name}")
85
+ Logger.debug(LogCategory.CAMPAIGN, "Campaign \"${name}\": targeting rules not satisfied")
86
+ Logger.debug(LogCategory.CAMPAIGN, "Campaign \"${name}\": trigger MATCHED for event ${event.name}")
87
+ Logger.info(LogCategory.CAMPAIGN, "Campaign \"${name}\": MATCH - executing action")
88
+ ```
89
+
90
+ #### Modified: `library/src/androidMain/kotlin/tools/amply/sdk/Amply.kt`
91
+
92
+ Added logging API methods:
93
+
94
+ ```kotlin
95
+ fun setLogLevel(level: LogLevel)
96
+ fun setLogLevel(level: String?)
97
+ fun getLogLevel(): LogLevel
98
+ fun setLogListener(listener: LogListener?)
99
+ ```
100
+
101
+ #### Modified: `library/src/iosMain/kotlin/tools/amply/sdk/Amply.kt`
102
+
103
+ Added same logging API methods as Android.
104
+
105
+ ---
106
+
107
+ ### 2. React Native SDK (react-native-sdk)
108
+
109
+ #### Modified: `src/nativeSpecs/NativeAmplyModule.ts`
110
+
111
+ Added TypeScript types and TurboModule methods:
112
+
113
+ ```typescript
114
+ // New type
115
+ export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
116
+
117
+ // Extended AmplyInitializationConfig
118
+ export type AmplyInitializationConfig = {
119
+ // ... existing fields ...
120
+ debug?: boolean | null; // Shorthand for logLevel: 'debug'
121
+ logLevel?: LogLevel | null; // Takes precedence over debug
122
+ };
123
+
124
+ // New methods in Spec
125
+ setLogLevel(level: string): void;
126
+ getLogLevel(): string;
127
+ ```
128
+
129
+ #### Modified: `src/index.ts`
130
+
131
+ Added JS-side debug log handling:
132
+
133
+ ```typescript
134
+ // Debug log listener that outputs to Metro console
135
+ function ensureDebugLogListener(): void {
136
+ addSystemEventListenerInternal((event) => {
137
+ if (event.name === 'DebugLog') {
138
+ // Format and output to appropriate console method
139
+ const level = event.properties.level;
140
+ switch (level) {
141
+ case 'error': console.error(formattedLog); break;
142
+ case 'warn': console.warn(formattedLog); break;
143
+ case 'debug': console.debug(formattedLog); break;
144
+ default: console.log(formattedLog);
145
+ }
146
+ }
147
+ });
148
+ }
149
+
150
+ // Exported functions
151
+ export function setLogLevel(level: LogLevel): void;
152
+ export function getLogLevel(): LogLevel;
153
+ ```
154
+
155
+ #### Modified: `android/src/main/java/tools/amply/sdk/reactnative/model/AmplyInitializationOptions.kt`
156
+
157
+ Added LogLevel enum and config parsing:
158
+
159
+ ```kotlin
160
+ enum class LogLevel(val level: Int) {
161
+ NONE(0), ERROR(1), WARN(2), INFO(3), DEBUG(4);
162
+
163
+ companion object {
164
+ fun fromString(value: String?): LogLevel
165
+ }
166
+ }
167
+
168
+ data class AmplyInitializationOptions(
169
+ // ... existing fields ...
170
+ val debug: Boolean?,
171
+ val logLevel: LogLevel?,
172
+ ) {
173
+ fun getEffectiveLogLevel(): LogLevel
174
+ }
175
+ ```
176
+
177
+ #### Modified: `android/src/main/java/tools/amply/sdk/reactnative/core/AmplyClient.kt`
178
+
179
+ Added interface methods:
180
+
181
+ ```kotlin
182
+ val logEvents: SharedFlow<EventEnvelope>
183
+ fun setLogLevel(level: LogLevel)
184
+ fun getLogLevel(): LogLevel
185
+ ```
186
+
187
+ #### Modified: `android/src/main/java/tools/amply/sdk/reactnative/core/DefaultAmplyClient.kt`
188
+
189
+ - Set up LogListener during initialization to forward logs to JS
190
+ - Implemented setLogLevel/getLogLevel methods
191
+ - Added logEvents SharedFlow
192
+
193
+ #### Modified: `android/src/main/java/tools/amply/sdk/reactnative/AmplyModule.kt`
194
+
195
+ - Added log event collection job
196
+ - Implemented setLogLevel/getLogLevel TurboModule methods
197
+ - Forward log events to JS via emitOnSystemEvent
198
+
199
+ #### Modified: `ios/Sources/AmplyReactNative/AmplyModule.mm`
200
+
201
+ - Added AmplyLogLevel enum and helper functions
202
+ - Implemented ASDKLogListener protocol
203
+ - Added setLogLevel/getLogLevel methods
204
+ - Added registerLogListenerInternal for log forwarding
205
+ - Implemented ASDKSystemEventsListener for system events (fixes iOS gap)
206
+
207
+ ---
208
+
209
+ ## Usage
210
+
211
+ ### Initialize with Debug Mode
212
+
213
+ ```typescript
214
+ import Amply from '@anthropic/react-native-amply-sdk';
215
+
216
+ // Option 1: Simple debug flag
217
+ await Amply.initialize({
218
+ appId: 'my-app-id',
219
+ apiKeyPublic: 'my-public-key',
220
+ debug: true, // Enables all logging
221
+ });
222
+
223
+ // Option 2: Specific log level
224
+ await Amply.initialize({
225
+ appId: 'my-app-id',
226
+ apiKeyPublic: 'my-public-key',
227
+ logLevel: 'info', // Only info, warn, error
228
+ });
229
+ ```
230
+
231
+ ### Change Log Level at Runtime
232
+
233
+ ```typescript
234
+ // Enable debug logging
235
+ Amply.setLogLevel('debug');
236
+
237
+ // Disable logging
238
+ Amply.setLogLevel('none');
239
+
240
+ // Check current level
241
+ const level = Amply.getLogLevel(); // 'debug'
242
+ ```
243
+
244
+ ### Console Output Example
245
+
246
+ When debug mode is enabled, developers see logs in Metro console:
247
+
248
+ ```
249
+ [Amply INFO] [sdk] SDK initializing with appId=my-app-id
250
+ [Amply INFO] [config] Config fetch started
251
+ [Amply INFO] [config] Config fetch completed: 3 campaigns loaded
252
+ [Amply DEBUG] [campaign] Campaign loaded: "Welcome Offer" (camp_123)
253
+ [Amply DEBUG] [campaign] Evaluating 3 campaigns for event: ButtonTapped
254
+ [Amply DEBUG] [campaign] Campaign "rate_review": targeting rules not satisfied
255
+ [Amply DEBUG] [campaign] Campaign "welcome_offer": trigger MATCHED for event ButtonTapped
256
+ [Amply INFO] [campaign] Campaign "welcome_offer": MATCH - executing action
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Log Level Hierarchy
262
+
263
+ | Level | Value | Includes |
264
+ |-------|-------|----------|
265
+ | `none` | 0 | Nothing |
266
+ | `error` | 1 | Errors only |
267
+ | `warn` | 2 | Errors + Warnings |
268
+ | `info` | 3 | Errors + Warnings + Info (SDK lifecycle) |
269
+ | `debug` | 4 | Everything (including campaign evaluation) |
270
+
271
+ ---
272
+
273
+ ## Data Flow
274
+
275
+ ```
276
+ KMP SDK (Kotlin)
277
+
278
+ ├── Logger.log() called
279
+ │ │
280
+ │ ├── Check if level enabled
281
+ │ │ │
282
+ │ │ └── If enabled: Create LogEntry
283
+ │ │ │
284
+ │ │ ├── Print to native console (always)
285
+ │ │ │
286
+ │ │ └── Call listener.onLog(entry)
287
+ │ │
288
+ └── LogListener (set by RN wrapper)
289
+
290
+ └── Convert to EventEnvelope
291
+
292
+ ├── Android: emit to logEvents SharedFlow
293
+ │ │
294
+ │ └── AmplyModule collects and emits to JS
295
+
296
+ └── iOS: call emitOnSystemEvent directly
297
+
298
+ └── JS receives via onSystemEvent
299
+
300
+ └── If event.name === 'DebugLog'
301
+
302
+ └── Output to console.log/warn/error/debug
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Known Issues / TODO
308
+
309
+ 1. **KMP SDK Tests Failing**: Some session-related tests are failing after the changes. Need to investigate if this is related to Logger import or pre-existing issues.
310
+
311
+ 2. **iOS Log Listener Protocol**: The `ASDKLogListener` protocol may not be exported from the KMP SDK XCFramework yet. Will need to verify after building the iOS framework.
312
+
313
+ 3. **Codegen**: The TurboModule codegen needs to be run during native build to generate the setLogLevel/getLogLevel bindings.
314
+
315
+ ---
316
+
317
+ ## Improvements Over Original Request
318
+
319
+ | Original Issue | Resolution |
320
+ |----------------|------------|
321
+ | Option A vs B undecided | Used Option A (System Events) - logs forwarded via existing channel |
322
+ | `debug` + `logLevel` conflict | `logLevel` takes precedence; `debug: true` is alias for `logLevel: 'debug'` |
323
+ | KMP SDK has no logging framework | Created `Logger` singleton with levels, categories, and listener interface |
324
+ | Campaign evaluation silent | Added detailed logging in `CampaignManager.activate()` |
325
+ | iOS system events gap | Fixed - iOS now properly implements `ASDKSystemEventsListener` |
326
+ | Centralized logging | All logs originate from KMP SDK, ensuring consistency |
@@ -11,9 +11,46 @@
11
11
 
12
12
  using namespace facebook::react;
13
13
 
14
- @interface Amply : NativeAmplyModuleSpecBase <NativeAmplyModuleSpec, ASDKDeepLinkListener>
14
+ /**
15
+ * Log level enum for SDK debug output.
16
+ */
17
+ typedef NS_ENUM(NSInteger, AmplyLogLevel) {
18
+ AmplyLogLevelNone = 0,
19
+ AmplyLogLevelError = 1,
20
+ AmplyLogLevelWarn = 2,
21
+ AmplyLogLevelInfo = 3,
22
+ AmplyLogLevelDebug = 4
23
+ };
24
+
25
+ static AmplyLogLevel AmplyLogLevelFromString(NSString *level) {
26
+ if (!level) return AmplyLogLevelNone;
27
+ NSString *lowercased = [level lowercaseString];
28
+ if ([lowercased isEqualToString:@"none"]) return AmplyLogLevelNone;
29
+ if ([lowercased isEqualToString:@"error"]) return AmplyLogLevelError;
30
+ if ([lowercased isEqualToString:@"warn"]) return AmplyLogLevelWarn;
31
+ if ([lowercased isEqualToString:@"info"]) return AmplyLogLevelInfo;
32
+ if ([lowercased isEqualToString:@"debug"]) return AmplyLogLevelDebug;
33
+ return AmplyLogLevelNone;
34
+ }
35
+
36
+ static NSString* AmplyLogLevelToString(AmplyLogLevel level) {
37
+ switch (level) {
38
+ case AmplyLogLevelNone: return @"none";
39
+ case AmplyLogLevelError: return @"error";
40
+ case AmplyLogLevelWarn: return @"warn";
41
+ case AmplyLogLevelInfo: return @"info";
42
+ case AmplyLogLevelDebug: return @"debug";
43
+ }
44
+ return @"none";
45
+ }
46
+
47
+ // Note: ASDKLogListener is not yet available in KMP SDK iOS XCFramework
48
+ // Logging support will be enabled when the KMP SDK iOS exports these APIs
49
+ @interface Amply : NativeAmplyModuleSpecBase <NativeAmplyModuleSpec, ASDKDeepLinkListener, ASDKSystemEventsListener>
15
50
  @property (nonatomic, strong) ASDKAmply *amplyInstance;
16
51
  @property (nonatomic, assign) BOOL deepLinkListenerRegistered;
52
+ @property (nonatomic, assign) BOOL systemEventsListenerRegistered;
53
+ @property (nonatomic, assign) AmplyLogLevel currentLogLevel;
17
54
  @end
18
55
 
19
56
  @implementation Amply
@@ -66,6 +103,31 @@ RCT_EXPORT_MODULE()
66
103
  // Create Amply instance
67
104
  self.amplyInstance = [[ASDKAmply alloc] initWithConfig:amplyConfig];
68
105
 
106
+ // Parse debug and logLevel options
107
+ std::optional<bool> debugOpt = config.debug();
108
+ BOOL debug = debugOpt.has_value() && debugOpt.value();
109
+ NSString *logLevelStr = config.logLevel();
110
+
111
+ // Resolve effective log level: logLevel takes precedence over debug
112
+ if (logLevelStr && logLevelStr.length > 0) {
113
+ self.currentLogLevel = AmplyLogLevelFromString(logLevelStr);
114
+ } else if (debug) {
115
+ self.currentLogLevel = AmplyLogLevelDebug;
116
+ } else {
117
+ self.currentLogLevel = AmplyLogLevelNone;
118
+ }
119
+
120
+ if (self.currentLogLevel != AmplyLogLevelNone) {
121
+ RCTLogInfo(@"[AmplyReactNative] Debug logging enabled at level: %@", AmplyLogLevelToString(self.currentLogLevel));
122
+ }
123
+
124
+ // TODO: Enable when KMP SDK iOS exports setLogLevel and setLogListener
125
+ // [self.amplyInstance setLogLevelLevel:AmplyLogLevelToString(self.currentLogLevel)];
126
+ // [self registerLogListenerInternal];
127
+
128
+ // Register system events listener
129
+ [self registerSystemEventsListenerInternal];
130
+
69
131
  RCTLogInfo(@"[AmplyReactNative] Initialized with appId=%@", appId);
70
132
 
71
133
  if (resolve) {
@@ -324,6 +386,56 @@ RCT_EXPORT_MODULE()
324
386
  RCTLogInfo(@"[AmplyReactNative] removeListeners called with count: %f", count);
325
387
  }
326
388
 
389
+ - (void)setLogLevel:(NSString *)level
390
+ {
391
+ self.currentLogLevel = AmplyLogLevelFromString(level);
392
+ RCTLogInfo(@"[AmplyReactNative] Log level set to: %@", level);
393
+ // TODO: Enable when KMP SDK iOS exports setLogLevel
394
+ // if (self.amplyInstance) {
395
+ // [self.amplyInstance setLogLevelLevel:level];
396
+ // }
397
+ }
398
+
399
+ - (NSString *)getLogLevel
400
+ {
401
+ return AmplyLogLevelToString(self.currentLogLevel);
402
+ }
403
+
404
+ #pragma mark - Internal Listener Registration
405
+
406
+ - (void)registerSystemEventsListenerInternal
407
+ {
408
+ if (self.systemEventsListenerRegistered) {
409
+ return;
410
+ }
411
+ if (!self.amplyInstance) {
412
+ return;
413
+ }
414
+ [self.amplyInstance setSystemEventsListenerListener:self];
415
+ self.systemEventsListenerRegistered = YES;
416
+ RCTLogInfo(@"[AmplyReactNative] System events listener registered");
417
+ }
418
+
419
+ #pragma mark - ASDKSystemEventsListener
420
+
421
+ - (void)onEventEvent:(id<ASDKEventInterface>)event
422
+ {
423
+ RCTLogInfo(@"[AmplyReactNative] System event received: %@", event.name);
424
+
425
+ NSDictionary *payload = @{
426
+ @"name": event.name ?: @"",
427
+ @"type": @"system",
428
+ @"timestamp": @(event.timestamp),
429
+ @"properties": event.properties ?: @{}
430
+ };
431
+
432
+ [self emitOnSystemEvent:payload];
433
+ }
434
+
435
+ // TODO: Enable ASDKLogListener when KMP SDK iOS exports logging APIs
436
+ // #pragma mark - ASDKLogListener
437
+ // - (void)onLogEntry:(ASDKLogEntry *)entry { ... }
438
+
327
439
  - (std::shared_ptr<TurboModule>)getTurboModule:(const ObjCTurboModule::InitParams &)params
328
440
  {
329
441
  return std::make_shared<NativeAmplyModuleSpecJSI>(params);
@@ -68,6 +68,14 @@ namespace facebook::react {
68
68
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "registerDeepLinkListener", @selector(registerDeepLinkListener), args, count);
69
69
  }
70
70
 
71
+ static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_setLogLevel(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
72
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "setLogLevel", @selector(setLogLevel:), args, count);
73
+ }
74
+
75
+ static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_getLogLevel(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
76
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, StringKind, "getLogLevel", @selector(getLogLevel), args, count);
77
+ }
78
+
71
79
  static facebook::jsi::Value __hostFunction_NativeAmplyModuleSpecJSI_addListener(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
72
80
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "addListener", @selector(addListener:), args, count);
73
81
  }
@@ -95,8 +103,14 @@ namespace facebook::react {
95
103
 
96
104
 
97
105
  methodMap_["registerDeepLinkListener"] = MethodMetadata {0, __hostFunction_NativeAmplyModuleSpecJSI_registerDeepLinkListener};
98
-
99
-
106
+
107
+
108
+ methodMap_["setLogLevel"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_setLogLevel};
109
+
110
+
111
+ methodMap_["getLogLevel"] = MethodMetadata {0, __hostFunction_NativeAmplyModuleSpecJSI_getLogLevel};
112
+
113
+
100
114
  methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeAmplyModuleSpecJSI_addListener};
101
115
 
102
116
 
@@ -41,6 +41,8 @@ namespace JS {
41
41
  NSString *endpoint() const;
42
42
  id<NSObject> _Nullable datasetPrefetch() const;
43
43
  NSString *defaultConfig() const;
44
+ std::optional<bool> debug() const;
45
+ NSString *logLevel() const;
44
46
 
45
47
  AmplyInitializationConfig(NSDictionary *const v) : _v(v) {}
46
48
  private:
@@ -84,6 +86,8 @@ namespace JS {
84
86
  resolve:(RCTPromiseResolveBlock)resolve
85
87
  reject:(RCTPromiseRejectBlock)reject;
86
88
  - (void)registerDeepLinkListener;
89
+ - (void)setLogLevel:(NSString *)level;
90
+ - (NSString *)getLogLevel;
87
91
  - (void)addListener:(NSString *)eventName;
88
92
  - (void)removeListeners:(double)count;
89
93
 
@@ -138,6 +142,16 @@ inline NSString *JS::NativeAmplyModule::AmplyInitializationConfig::defaultConfig
138
142
  id const p = _v[@"defaultConfig"];
139
143
  return RCTBridgingToOptionalString(p);
140
144
  }
145
+ inline std::optional<bool> JS::NativeAmplyModule::AmplyInitializationConfig::debug() const
146
+ {
147
+ id const p = _v[@"debug"];
148
+ return RCTBridgingToOptionalBool(p);
149
+ }
150
+ inline NSString *JS::NativeAmplyModule::AmplyInitializationConfig::logLevel() const
151
+ {
152
+ id const p = _v[@"logLevel"];
153
+ return RCTBridgingToOptionalString(p);
154
+ }
141
155
  inline NSString *JS::NativeAmplyModule::TrackEventPayload::name() const
142
156
  {
143
157
  id const p = _v[@"name"];