react-native-sdk-pianoio 0.3.7 → 0.3.8

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.
@@ -1,6 +1,7 @@
1
1
  package com.sdkpianoio
2
2
 
3
3
  import android.content.Context
4
+ import android.util.Log
4
5
  import com.facebook.react.bridge.Promise
5
6
  import io.piano.android.composer.Composer
6
7
  import io.piano.android.composer.listeners.EventTypeListener
@@ -31,6 +32,7 @@ class ComposerPianoImpl {
31
32
 
32
33
  companion object {
33
34
  var aid: String = ""
35
+ private const val TAG = "ComposerPianoImpl"
34
36
  }
35
37
 
36
38
  // MARK: - Configuration Properties
@@ -45,7 +47,7 @@ class ComposerPianoImpl {
45
47
  // MARK: - Initialization
46
48
 
47
49
  constructor() {
48
- println("ComposerPianoImpl: Constructor initialized")
50
+ Log.d(TAG, "Constructor initialized")
49
51
  }
50
52
 
51
53
  /**
@@ -55,36 +57,35 @@ class ComposerPianoImpl {
55
57
  * @param aid The Piano Application ID.
56
58
  */
57
59
  fun initializeComposer(context: Context, aid: String, isSandbox: Boolean) {
58
- println("ComposerPianoImpl: Initializing composer with AID: $aid")
60
+ Log.d(TAG, "Initializing composer with AID: $aid, isSandbox: $isSandbox")
59
61
 
60
62
  // Validate input parameter
61
63
  if (aid.isBlank()) {
64
+ Log.e(TAG, "AID cannot be empty")
62
65
  throw IllegalArgumentException("AID cannot be empty")
63
66
  }
64
67
 
65
68
  // Configure static AID for the whole class
66
69
  ComposerPianoImpl.aid = aid
67
- println("ComposerPianoImpl: Static AID configured: ${ComposerPianoImpl.aid}")
70
+ Log.d(TAG, "Static AID configured: ${ComposerPianoImpl.aid}")
68
71
 
69
72
  try {
70
- // 1. Initialize the Composer SDK statically using the official API
71
73
  val endpoint =
72
74
  if (isSandbox) Composer.Endpoint.SANDBOX else Composer.Endpoint.PRODUCTION
75
+ Log.d(TAG, "Using endpoint: $endpoint")
73
76
  Composer.init(context, aid, endpoint)
74
- println("ComposerPianoImpl: Composer SDK initialized for PRODUCTION")
77
+ Log.d(TAG, "Composer SDK initialized")
75
78
 
76
- // 2. Get the singleton instance and store it
77
79
  this.composer = Composer.getInstance()
78
- println("ComposerPianoImpl: Composer instance retrieved")
80
+ Log.d(TAG, "Composer instance retrieved")
79
81
 
80
- // 3. Initialize the TokenService and store it as a property
81
82
  this.tokenService = TokenService()
82
83
  this.tokenService?.initialize(aid)
83
- println("ComposerPianoImpl: TokenService initialized")
84
+ Log.d(TAG, "TokenService initialized")
84
85
 
85
- println("ComposerPianoImpl: Initialization completed successfully")
86
+ Log.d(TAG, "Initialization completed successfully")
86
87
  } catch (e: Exception) {
87
- println("ComposerPianoImpl: Error during initialization: ${e.message}")
88
+ Log.e(TAG, "Error during initialization", e)
88
89
  throw e
89
90
  }
90
91
  }
@@ -126,12 +127,14 @@ class ComposerPianoImpl {
126
127
  // MARK: - Configuration Methods (Setters)
127
128
 
128
129
  fun addTag(tag: String) {
130
+ Log.d(TAG, "Adding tag: $tag")
129
131
  if (!this.tags.contains(tag)) {
130
132
  this.tags.add(tag)
131
133
  }
132
134
  }
133
135
 
134
136
  fun addTags(tags: List<String>) {
137
+ Log.d(TAG, "Adding tags: $tags")
135
138
  for (tag in tags) {
136
139
  if (!this.tags.contains(tag)) {
137
140
  this.tags.add(tag)
@@ -140,27 +143,33 @@ class ComposerPianoImpl {
140
143
  }
141
144
 
142
145
  fun setZoneId(zoneId: String) {
146
+ Log.d(TAG, "Setting zoneId: $zoneId")
143
147
  this.zoneId = zoneId
144
148
  }
145
149
 
146
150
  fun setReferrer(referrer: String) {
151
+ Log.d(TAG, "Setting referrer: $referrer")
147
152
  this.referrer = referrer
148
153
  }
149
154
 
150
155
  fun setUrl(url: String) {
156
+ Log.d(TAG, "Setting url: $url")
151
157
  this.url = url
152
158
  }
153
159
 
154
160
  fun setUserToken(token: String) {
161
+ Log.d(TAG, "Setting userToken")
155
162
  this.userToken = token
156
163
  composer?.userToken(token)
157
164
  }
158
165
 
159
166
  fun addCustomVariable(key: String, value: String) {
167
+ Log.d(TAG, "Adding custom variable: $key = $value")
160
168
  this.customVariables[key] = value
161
169
  }
162
170
 
163
171
  fun setCustomVariables(variables: Map<String, String>) {
172
+ Log.d(TAG, "Setting custom variables")
164
173
  this.customVariables.clear()
165
174
  this.customVariables.putAll(variables)
166
175
  }
@@ -168,22 +177,27 @@ class ComposerPianoImpl {
168
177
  // MARK: - Cleanup Methods
169
178
 
170
179
  fun clearTags() {
180
+ Log.d(TAG, "Clearing tags")
171
181
  this.tags.clear()
172
182
  }
173
183
 
174
184
  fun clearCustomVariables() {
185
+ Log.d(TAG, "Clearing custom variables")
175
186
  this.customVariables.clear()
176
187
  }
177
188
 
178
189
  fun removeTag(tag: String) {
190
+ Log.d(TAG, "Removing tag: $tag")
179
191
  this.tags.remove(tag)
180
192
  }
181
193
 
182
194
  fun removeCustomVariable(key: String) {
195
+ Log.d(TAG, "Removing custom variable: $key")
183
196
  this.customVariables.remove(key)
184
197
  }
185
198
 
186
199
  fun clearConfiguration() {
200
+ Log.d(TAG, "Clearing all configurations")
187
201
  this.clearTags()
188
202
  this.clearCustomVariables()
189
203
  this.zoneId = null
@@ -201,25 +215,25 @@ class ComposerPianoImpl {
201
215
  * @throws IllegalStateException if the composer has not been initialized.
202
216
  */
203
217
  fun executeComposer(promise: Promise) {
204
- println("ComposerPianoImpl: Starting composer execution")
218
+ Log.d(TAG, "Starting composer execution")
205
219
 
206
- // 1. Validate that the composer has been initialized
207
220
  val composerInstance = composer
208
221
  if (composerInstance == null) {
209
222
  val error =
210
223
  IllegalStateException(
211
224
  "Composer not initialized. Call initializeComposer() first."
212
225
  )
213
- println("ComposerPianoImpl: Error - Composer not initialized")
226
+ Log.e(TAG, "executeComposer failed: Composer not initialized")
214
227
  promise.reject("INIT_ERROR", error.message, error)
215
228
  return
216
229
  }
217
230
 
218
231
  try {
219
232
  val customParams = CustomParameters()
233
+ Log.d(TAG, "Custom params are: $customParams")
220
234
  this.customVariables.forEach { (key, value) -> customParams.request(key, value) }
235
+ Log.d(TAG, "Custom variables are $customVariables")
221
236
 
222
- // 2. Build the ExperienceRequest with all stored configurations
223
237
  val request =
224
238
  ExperienceRequest.Builder()
225
239
  .tags(this.tags)
@@ -229,38 +243,46 @@ class ComposerPianoImpl {
229
243
  .customParams(customParams)
230
244
  .debug(false)
231
245
  .build()
232
- println("ComposerPianoImpl: ExperienceRequest created successfully")
246
+ Log.d(TAG, "ExperienceRequest created successfully")
233
247
 
234
- // 3. Define the listeners to handle events from the SDK
235
248
  val listeners: List<EventTypeListener<*>> =
236
249
  listOf(
237
250
  UserSegmentListener { event ->
238
- println("UserSegmentListener event: ${event.eventData}")
239
- // You can process user segment data here if needed
251
+ Log.d(
252
+ TAG,
253
+ "Listener received: UserSegmentListener event: ${event.eventData}"
254
+ )
240
255
  },
241
256
  MeterListener { event ->
242
- println("MeterListener event: ${event.eventData}")
257
+ Log.d(
258
+ TAG,
259
+ "Listener received: MeterListener event: ${event.eventData}"
260
+ )
243
261
  promise.resolve(event.eventData.toString())
244
262
  },
245
263
  ShowTemplateListener { event: Event<ShowTemplate> ->
246
- println("ShowTemplateListener event: ${event.eventData}")
264
+ Log.d(
265
+ TAG,
266
+ "Listener received: ShowTemplateListener event: ${event.eventData}"
267
+ )
247
268
  promise.resolve(event.eventData.toString())
248
269
  },
249
270
  NonSiteListener { event ->
250
- println("NonSiteListener event: ${event.eventData}")
271
+ Log.d(
272
+ TAG,
273
+ "Listener received: NonSiteListener event: ${event.eventData}"
274
+ )
251
275
  }
252
276
  )
277
+ Log.d(TAG, "Event listeners created")
253
278
 
254
- println("ComposerPianoImpl: Event listeners created")
255
-
256
- // 4. Execute the experience request using the real Piano SDK method
257
279
  composerInstance.getExperience(request, listeners) { exception ->
258
- println("ComposerPianoImpl: SDK returned an exception: ${exception.message}")
280
+ Log.e(TAG, "getExperience failed with exception", exception)
259
281
  promise.reject("EXECUTION_ERROR", exception.message, exception)
260
282
  }
261
- println("ComposerPianoImpl: getExperience() called successfully")
283
+ Log.d(TAG, "getExperience() called successfully")
262
284
  } catch (e: Exception) {
263
- println("ComposerPianoImpl: Error during execution setup: ${e.message}")
285
+ Log.e(TAG, "Error during execution setup", e)
264
286
  promise.reject("SETUP_ERROR", e.message, e)
265
287
  }
266
288
  }
@@ -1,5 +1,6 @@
1
1
  package com.sdkpianoio
2
2
 
3
+ import android.util.Log
3
4
  import androidx.activity.ComponentActivity
4
5
  import androidx.activity.result.ActivityResultLauncher
5
6
  import com.facebook.react.bridge.Arguments
@@ -22,74 +23,91 @@ import io.piano.android.id.models.PianoIdAuthSuccessResult
22
23
  * This module serves as the bridge between JavaScript and the native Android implementation.
23
24
  */
24
25
  @ReactModule(name = SdkPianoioModule.NAME)
25
- class SdkPianoioModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
26
-
26
+ class SdkPianoioModule(private val reactContext: ReactApplicationContext) :
27
+ ReactContextBaseJavaModule(reactContext) {
28
+
27
29
  private var composerImpl: ComposerPianoImpl? = null
28
30
  private var isInitialized: Boolean = false
29
31
  private var signInPromise: Promise? = null
30
32
 
31
33
  private lateinit var authResultLauncher: ActivityResultLauncher<PianoIdClient.SignInContext>
32
34
 
35
+ companion object {
36
+ const val NAME = "SdkPianoio"
37
+ private const val TAG = "SdkPianoioModule"
38
+ }
39
+
33
40
  init {
34
- // The ActivityResultLauncher must be registered in the init block or onCreate.
35
- // We need to ensure we have a ComponentActivity to access the registry.
41
+ Log.d(TAG, "Initializing SdkPianoioModule")
36
42
  val activity = reactContext.currentActivity
37
43
  if (activity is ComponentActivity) {
38
- authResultLauncher = activity.activityResultRegistry.register(
39
- "piano-id-login",
40
- PianoIdAuthResultContract()
41
- ) { result ->
42
- val promise = signInPromise
43
- signInPromise = null // Clean up promise immediately
44
- when (result) {
45
- is PianoIdAuthSuccessResult -> {
46
- val token = result.token
47
- composerImpl?.tokenService?.setToken(token)
48
- val user = composerImpl?.tokenService?.getCurrentUser()
49
- if (user != null) {
50
- val resultMap = Arguments.createMap().apply {
51
- putBoolean("success", true)
52
- putString("userId", user.id)
53
- putString("userEmail", user.email)
54
- putString("accessToken", user.accessToken)
44
+ authResultLauncher =
45
+ activity.activityResultRegistry.register(
46
+ "piano-id-login",
47
+ PianoIdAuthResultContract()
48
+ ) { result ->
49
+ Log.d(TAG, "ActivityResultLauncher received a result.")
50
+ val promise = signInPromise
51
+ signInPromise = null
52
+ when (result) {
53
+ is PianoIdAuthSuccessResult -> {
54
+ Log.d(TAG, "Sign-in successful.")
55
+ val token = result.token
56
+ composerImpl?.tokenService?.setToken(token)
57
+ val user = composerImpl?.tokenService?.getCurrentUser()
58
+ if (user != null) {
59
+ val resultMap =
60
+ Arguments.createMap().apply {
61
+ putBoolean("success", true)
62
+ putString("userId", user.id)
63
+ putString("userEmail", user.email)
64
+ putString("accessToken", user.accessToken)
65
+ }
66
+ promise?.resolve(resultMap)
67
+ } else {
68
+ Log.e(TAG, "Failed to construct user after login.")
69
+ promise?.reject(
70
+ "SIGNIN_ERROR",
71
+ "Failed to construct user after login."
72
+ )
73
+ }
74
+ }
75
+ is PianoIdAuthFailureResult -> {
76
+ val exception = result.exception
77
+ Log.e(TAG, "Sign-in failed", exception)
78
+ promise?.reject("SIGNIN_ERROR", exception.message, exception)
79
+ }
80
+ null -> {
81
+ Log.w(TAG, "Sign-in cancelled by user.")
82
+ promise?.reject("CANCELLED", "User cancelled the sign-in process.")
55
83
  }
56
- promise?.resolve(resultMap)
57
- } else {
58
- promise?.reject("SIGNIN_ERROR", "Failed to construct user after login.")
59
84
  }
60
85
  }
61
- is PianoIdAuthFailureResult -> {
62
- val exception = result.exception
63
- promise?.reject("SIGNIN_ERROR", exception.message, exception)
64
- }
65
- null -> {
66
- // This handles the case where the user cancels the flow
67
- promise?.reject("CANCELLED", "User cancelled the sign-in process.")
68
- }
69
- }
70
- }
71
86
  } else {
72
- // This is a fallback, though in a normal RN app, currentActivity should be a ComponentActivity.
73
- // You might want to log a warning here.
87
+ Log.e(TAG, "Activity not available for registration. SignIn will not work.")
74
88
  }
75
89
  }
76
-
90
+
77
91
  override fun getName(): String {
78
92
  return NAME
79
93
  }
80
-
94
+
81
95
  @ReactMethod
82
96
  fun initialize(aid: String, isSandbox: Boolean, promise: Promise) {
97
+ Log.d(TAG, "initialize called with AID: $aid, isSandbox: $isSandbox")
83
98
  try {
84
99
  if (isInitialized) {
100
+ Log.d(TAG, "Already initialized.")
85
101
  promise.resolve(true)
86
102
  return
87
103
  }
88
104
  composerImpl = ComposerPianoImpl()
89
105
  composerImpl?.initializeComposer(reactContext, aid, isSandbox)
90
106
  isInitialized = true
107
+ Log.d(TAG, "Initialization successful.")
91
108
  promise.resolve(true)
92
109
  } catch (e: Exception) {
110
+ Log.e(TAG, "Initialization failed", e)
93
111
  promise.reject("INIT_ERROR", "Failed to initialize Piano SDK", e)
94
112
  }
95
113
  }
@@ -98,86 +116,103 @@ class SdkPianoioModule(private val reactContext: ReactApplicationContext) : Reac
98
116
 
99
117
  @ReactMethod
100
118
  fun addTag(tag: String, promise: Promise) {
119
+ Log.d(TAG, "addTag called with: $tag")
101
120
  try {
102
121
  if (!validateInitialization(promise) || !validateParameter(tag, "tag", promise)) return
103
122
  composerImpl?.addTag(tag)
104
123
  promise.resolve(true)
105
124
  } catch (e: Exception) {
125
+ Log.e(TAG, "addTag failed", e)
106
126
  promise.reject("ADD_TAG_ERROR", "Error adding tag", e)
107
127
  }
108
128
  }
109
129
 
110
130
  @ReactMethod
111
131
  fun addTags(tagsArray: ReadableArray, promise: Promise) {
132
+ Log.d(TAG, "addTags called")
112
133
  try {
113
- if (!validateInitialization(promise) || !validateArray(tagsArray, "tagsArray", promise)) return
134
+ if (!validateInitialization(promise) || !validateArray(tagsArray, "tagsArray", promise))
135
+ return
114
136
  val tags = tagsArray.toArrayList().mapNotNull { it?.toString() }
115
137
  composerImpl?.addTags(tags)
116
138
  promise.resolve(true)
117
139
  } catch (e: Exception) {
140
+ Log.e(TAG, "addTags failed", e)
118
141
  promise.reject("ADD_TAGS_ERROR", "Error adding tags", e)
119
142
  }
120
143
  }
121
144
 
122
145
  @ReactMethod
123
146
  fun setZoneId(zoneId: String, promise: Promise) {
147
+ Log.d(TAG, "setZoneId called with: $zoneId")
124
148
  try {
125
149
  if (!validateInitialization(promise)) return
126
150
  composerImpl?.setZoneId(zoneId)
127
151
  promise.resolve(true)
128
152
  } catch (e: Exception) {
153
+ Log.e(TAG, "setZoneId failed", e)
129
154
  promise.reject("SET_ZONEID_ERROR", "Error setting Zone ID", e)
130
155
  }
131
156
  }
132
157
 
133
158
  @ReactMethod
134
159
  fun setReferrer(referrer: String, promise: Promise) {
160
+ Log.d(TAG, "setReferrer called with: $referrer")
135
161
  try {
136
162
  if (!validateInitialization(promise)) return
137
163
  composerImpl?.setReferrer(referrer)
138
164
  promise.resolve(true)
139
165
  } catch (e: Exception) {
166
+ Log.e(TAG, "setReferrer failed", e)
140
167
  promise.reject("SET_REFERRER_ERROR", "Error setting referrer", e)
141
168
  }
142
169
  }
143
170
 
144
171
  @ReactMethod
145
172
  fun setUrl(url: String, promise: Promise) {
173
+ Log.d(TAG, "setUrl called with: $url")
146
174
  try {
147
175
  if (!validateInitialization(promise)) return
148
176
  composerImpl?.setUrl(url)
149
177
  promise.resolve(true)
150
178
  } catch (e: Exception) {
179
+ Log.e(TAG, "setUrl failed", e)
151
180
  promise.reject("SET_URL_ERROR", "Error setting URL", e)
152
181
  }
153
182
  }
154
183
 
155
184
  @ReactMethod
156
185
  fun setUserToken(token: String, promise: Promise) {
186
+ Log.d(TAG, "setUserToken called")
157
187
  try {
158
188
  if (!validateInitialization(promise)) return
159
189
  composerImpl?.setUserToken(token)
160
190
  promise.resolve(true)
161
191
  } catch (e: Exception) {
192
+ Log.e(TAG, "setUserToken failed", e)
162
193
  promise.reject("SET_TOKEN_ERROR", "Error setting user token", e)
163
194
  }
164
195
  }
165
196
 
166
197
  @ReactMethod
167
198
  fun addCustomVariable(key: String, value: String, promise: Promise) {
199
+ Log.d(TAG, "addCustomVariable called with: $key = $value")
168
200
  try {
169
201
  if (!validateInitialization(promise)) return
170
202
  composerImpl?.addCustomVariable(key, value)
171
203
  promise.resolve(true)
172
204
  } catch (e: Exception) {
205
+ Log.e(TAG, "addCustomVariable failed", e)
173
206
  promise.reject("ADD_CUSTOMVAR_ERROR", "Error adding custom variable", e)
174
207
  }
175
208
  }
176
-
209
+
177
210
  @ReactMethod
178
211
  fun setCustomVariables(variables: ReadableMap, promise: Promise) {
212
+ Log.d(TAG, "setCustomVariables called")
179
213
  try {
180
- if (!validateInitialization(promise) || !validateMap(variables, "variables", promise)) return
214
+ if (!validateInitialization(promise) || !validateMap(variables, "variables", promise))
215
+ return
181
216
  val map = mutableMapOf<String, String>()
182
217
  val iterator = variables.keySetIterator()
183
218
  while (iterator.hasNextKey()) {
@@ -194,6 +229,7 @@ class SdkPianoioModule(private val reactContext: ReactApplicationContext) : Reac
194
229
  composerImpl?.setCustomVariables(map)
195
230
  promise.resolve(true)
196
231
  } catch (e: Exception) {
232
+ Log.e(TAG, "setCustomVariables failed", e)
197
233
  promise.reject("SET_CUSTOMVARS_ERROR", "Error setting custom variables", e)
198
234
  }
199
235
  }
@@ -202,81 +238,97 @@ class SdkPianoioModule(private val reactContext: ReactApplicationContext) : Reac
202
238
 
203
239
  @ReactMethod
204
240
  fun executeExperience(promise: Promise) {
241
+ Log.d(TAG, "executeExperience called")
205
242
  try {
206
243
  if (!validateInitialization(promise)) return
207
244
  composerImpl?.executeComposer(promise)
208
245
  } catch (e: Exception) {
246
+ Log.e(TAG, "executeExperience failed", e)
209
247
  promise.reject("EXECUTION_ERROR", "Error executing experience", e)
210
248
  }
211
249
  }
212
250
 
213
251
  // MARK: - Authentication Methods
214
-
252
+
215
253
  @ReactMethod
216
254
  fun signIn(promise: Promise) {
255
+ Log.d(TAG, "signIn called")
217
256
  if (signInPromise != null) {
257
+ Log.w(TAG, "Sign-in already in progress.")
218
258
  promise.reject("IN_PROGRESS", "Another sign-in operation is already in progress.")
219
259
  return
220
260
  }
221
261
  try {
222
262
  if (!validateInitialization(promise)) return
223
263
  this.signInPromise = promise
264
+ Log.d(TAG, "Launching sign-in flow via TokenService")
224
265
  composerImpl?.tokenService?.signIn(authResultLauncher)
225
266
  } catch (e: Exception) {
267
+ Log.e(TAG, "signIn failed", e)
226
268
  promise.reject("SIGNIN_ERROR", "Error during sign in", e)
227
269
  this.signInPromise = null
228
270
  }
229
271
  }
230
-
272
+
231
273
  @ReactMethod
232
274
  fun signOut(promise: Promise) {
275
+ Log.d(TAG, "signOut called")
233
276
  try {
234
277
  if (!validateInitialization(promise)) return
235
278
  composerImpl?.tokenService?.signOut { success ->
236
279
  if (success) {
280
+ Log.d(TAG, "signOut successful")
237
281
  promise.resolve(Arguments.createMap().apply { putBoolean("success", true) })
238
282
  } else {
283
+ Log.w(TAG, "signOut failed")
239
284
  promise.reject("SIGNOUT_ERROR", "Sign out failed")
240
285
  }
241
286
  }
242
287
  } catch (e: Exception) {
288
+ Log.e(TAG, "signOut failed", e)
243
289
  promise.reject("SIGNOUT_ERROR", "Error during sign out", e)
244
290
  }
245
291
  }
246
-
292
+
247
293
  @ReactMethod
248
294
  fun getCurrentUser(promise: Promise) {
295
+ Log.d(TAG, "getCurrentUser called")
249
296
  try {
250
297
  if (!validateInitialization(promise)) return
251
298
  val user = composerImpl?.tokenService?.getCurrentUser()
252
299
  if (user != null) {
253
- val result = Arguments.createMap().apply {
254
- putBoolean("success", true)
255
- putString("userId", user.id)
256
- putString("userEmail", user.email)
257
- putString("accessToken", user.accessToken)
258
- putBoolean("authenticated", true)
259
- }
300
+ val result =
301
+ Arguments.createMap().apply {
302
+ putBoolean("success", true)
303
+ putString("userId", user.id)
304
+ putString("userEmail", user.email)
305
+ putString("accessToken", user.accessToken)
306
+ putBoolean("authenticated", true)
307
+ }
260
308
  promise.resolve(result)
261
309
  } else {
262
- val result = Arguments.createMap().apply {
263
- putBoolean("success", true)
264
- putBoolean("authenticated", false)
265
- }
310
+ val result =
311
+ Arguments.createMap().apply {
312
+ putBoolean("success", true)
313
+ putBoolean("authenticated", false)
314
+ }
266
315
  promise.resolve(result)
267
316
  }
268
317
  } catch (e: Exception) {
318
+ Log.e(TAG, "getCurrentUser failed", e)
269
319
  promise.reject("USER_ERROR", "Error getting current user", e)
270
320
  }
271
321
  }
272
-
322
+
273
323
  @ReactMethod
274
324
  fun isAuthenticated(promise: Promise) {
325
+ Log.d(TAG, "isAuthenticated called")
275
326
  try {
276
327
  if (!validateInitialization(promise)) return
277
328
  val isAuth = composerImpl?.tokenService?.isAuthenticated() ?: false
278
329
  promise.resolve(Arguments.createMap().apply { putBoolean("authenticated", isAuth) })
279
330
  } catch (e: Exception) {
331
+ Log.e(TAG, "isAuthenticated failed", e)
280
332
  promise.reject("AUTH_ERROR", "Error checking authentication", e)
281
333
  }
282
334
  }
@@ -285,18 +337,21 @@ class SdkPianoioModule(private val reactContext: ReactApplicationContext) : Reac
285
337
 
286
338
  @ReactMethod
287
339
  fun clearConfiguration(promise: Promise) {
340
+ Log.d(TAG, "clearConfiguration called")
288
341
  try {
289
342
  if (!validateInitialization(promise)) return
290
343
  composerImpl?.clearTags()
291
344
  composerImpl?.clearCustomVariables()
292
345
  promise.resolve(true)
293
346
  } catch (e: Exception) {
347
+ Log.e(TAG, "clearConfiguration failed", e)
294
348
  promise.reject("CLEAR_ERROR", "Error clearing configurations", e)
295
349
  }
296
350
  }
297
351
 
298
352
  private fun validateInitialization(promise: Promise): Boolean {
299
353
  if (!isInitialized || composerImpl == null) {
354
+ Log.w(TAG, "Validation failed: SDK not initialized.")
300
355
  promise.reject("NOT_INITIALIZED", "Piano SDK not initialized. Call initialize() first.")
301
356
  return false
302
357
  }
@@ -305,6 +360,7 @@ class SdkPianoioModule(private val reactContext: ReactApplicationContext) : Reac
305
360
 
306
361
  private fun validateParameter(value: String?, paramName: String, promise: Promise): Boolean {
307
362
  if (value.isNullOrBlank()) {
363
+ Log.w(TAG, "Validation failed: Parameter '$paramName' is null or blank.")
308
364
  promise.reject("INVALID_PARAMETER", "Parameter '$paramName' cannot be empty")
309
365
  return false
310
366
  }
@@ -313,6 +369,7 @@ class SdkPianoioModule(private val reactContext: ReactApplicationContext) : Reac
313
369
 
314
370
  private fun validateArray(array: ReadableArray?, paramName: String, promise: Promise): Boolean {
315
371
  if (array == null || array.size() == 0) {
372
+ Log.w(TAG, "Validation failed: Array '$paramName' is null or empty.")
316
373
  promise.reject("INVALID_PARAMETER", "Array '$paramName' cannot be empty")
317
374
  return false
318
375
  }
@@ -321,13 +378,10 @@ class SdkPianoioModule(private val reactContext: ReactApplicationContext) : Reac
321
378
 
322
379
  private fun validateMap(map: ReadableMap?, paramName: String, promise: Promise): Boolean {
323
380
  if (map == null) {
381
+ Log.w(TAG, "Validation failed: Map '$paramName' is null.")
324
382
  promise.reject("INVALID_PARAMETER", "Map '$paramName' cannot be empty")
325
383
  return false
326
384
  }
327
385
  return true
328
386
  }
329
-
330
- companion object {
331
- const val NAME = "SdkPianoio"
332
- }
333
387
  }
@@ -1,24 +1,18 @@
1
1
  package com.sdkpianoio
2
2
 
3
+ import android.util.Log
3
4
  import androidx.activity.result.ActivityResultLauncher
4
5
  import io.piano.android.id.PianoId
5
6
  import io.piano.android.id.PianoIdClient
6
7
  import io.piano.android.id.models.PianoIdToken
7
8
 
8
9
  /**
9
- * Represents a user from the Piano ID SDK.
10
- * This custom class simplifies passing user data back to React Native.
10
+ * Represents a user from the Piano ID SDK. This custom class simplifies passing user data back to
11
+ * React Native.
11
12
  */
12
13
  class User(val id: String, val email: String, val accessToken: String)
13
14
 
14
- /**
15
- * TokenService - Manages OAuth tokens and authentication with the Piano ID SDK.
16
- *
17
- * This class is responsible for:
18
- * - Integrating with the real PianoId SDK.
19
- * - Managing user authentication (sign-in, sign-out).
20
- * - Storing and providing the current user's token and information.
21
- */
15
+ /** TokenService - Manages OAuth tokens and authentication with the Piano ID SDK. */
22
16
  class TokenService {
23
17
 
24
18
  // MARK: - Properties
@@ -27,27 +21,28 @@ class TokenService {
27
21
  private var isInitialized: Boolean = false
28
22
  private var cachedToken: PianoIdToken? = null
29
23
 
24
+ companion object {
25
+ private const val TAG = "TokenService"
26
+ }
27
+
30
28
  // MARK: - Initialization
31
29
 
32
- /**
33
- * Initializes the Piano ID SDK. This should ideally be called once per application lifecycle.
34
- *
35
- * @param aid The Piano Application ID.
36
- */
37
30
  fun initialize(aid: String) {
38
- if (isInitialized) return
39
- println("TokenService: Initializing with AID: $aid")
31
+ if (isInitialized) {
32
+ Log.d(TAG, "Already initialized.")
33
+ return
34
+ }
35
+ Log.d(TAG, "Initializing with AID: $aid")
40
36
  try {
41
37
  if (aid.isBlank()) {
42
38
  throw IllegalArgumentException("AID cannot be empty")
43
39
  }
44
- // Initialize the SDK statically, then get the client instance.
45
40
  PianoId.init(PianoId.ENDPOINT_PRODUCTION, aid)
46
41
  this.client = PianoId.getInstance()
47
42
  isInitialized = true
48
- println("TokenService: Initialization completed successfully")
43
+ Log.d(TAG, "Initialization completed successfully")
49
44
  } catch (e: Exception) {
50
- println("TokenService: Error during initialization: ${e.message}")
45
+ Log.e(TAG, "Error during initialization", e)
51
46
  throw e
52
47
  }
53
48
  }
@@ -55,42 +50,44 @@ class TokenService {
55
50
  // MARK: - Authentication Methods
56
51
 
57
52
  fun setToken(token: PianoIdToken?) {
53
+ Log.d(TAG, "Setting token.")
58
54
  this.cachedToken = token
59
55
  }
60
56
 
61
57
  fun signIn(authResultLauncher: ActivityResultLauncher<PianoIdClient.SignInContext>) {
62
58
  val localClient = client
63
59
  if (localClient == null) {
64
- println("TokenService: Not initialized. Call initialize() first.")
60
+ Log.w(TAG, "signIn called but not initialized.")
65
61
  return
66
62
  }
67
- println("TokenService: Launching sign in flow")
68
- val request = localClient.signIn() // This returns a SignInContext
63
+ Log.d(TAG, "Launching sign in flow")
64
+ val request = localClient.signIn()
69
65
  authResultLauncher.launch(request)
70
66
  }
71
67
 
72
68
  fun signOut(callback: (Boolean) -> Unit) {
69
+ Log.d(TAG, "signOut called")
73
70
  val localClient = client
74
71
  if (localClient == null) {
75
- println("TokenService: Not initialized.")
72
+ Log.w(TAG, "signOut called but not initialized.")
76
73
  callback(false)
77
74
  return
78
75
  }
79
76
  val token = this.cachedToken?.accessToken
80
77
  if (token == null) {
81
- println("TokenService: No user is signed in.")
78
+ Log.d(TAG, "No user is signed in, considering signOut successful.")
82
79
  callback(true) // Already signed out
83
80
  return
84
81
  }
85
82
 
86
- println("TokenService: Starting sign out")
83
+ Log.d(TAG, "Starting sign out network request")
87
84
  localClient.signOut(token) {
88
85
  if (it.isSuccess) {
89
- println("TokenService: Sign out successful")
90
- this.cachedToken = null // Clear the cached token
86
+ Log.d(TAG, "Sign out successful")
87
+ this.cachedToken = null
91
88
  callback(true)
92
89
  } else {
93
- println("TokenService: Sign out failed: ${it.exceptionOrNull()?.message}")
90
+ Log.e(TAG, "Sign out failed", it.exceptionOrNull())
94
91
  callback(false)
95
92
  }
96
93
  }
@@ -99,16 +96,22 @@ class TokenService {
99
96
  // MARK: - User Information Methods
100
97
 
101
98
  fun getCurrentUser(): User? {
102
- val token = this.cachedToken ?: return null
103
- // Construct a user from the token information using the getInfoField method.
99
+ Log.d(TAG, "getCurrentUser called")
100
+ val token =
101
+ this.cachedToken
102
+ ?: run {
103
+ Log.d(TAG, "No cached token found, returning null user.")
104
+ return null
105
+ }
104
106
  return User(
105
- id = token.getInfoField<String>("user_id") ?: "unknown",
106
- email = token.getInfoField<String>("email") ?: "unknown",
107
- accessToken = token.accessToken
107
+ id = token.getInfoField<String>("user_id") ?: "unknown",
108
+ email = token.getInfoField<String>("email") ?: "unknown",
109
+ accessToken = token.accessToken
108
110
  )
109
111
  }
110
112
 
111
113
  fun isAuthenticated(): Boolean {
114
+ Log.d(TAG, "isAuthenticated called")
112
115
  return this.cachedToken != null
113
116
  }
114
117
  }
@@ -4,12 +4,6 @@ import PianoOAuth
4
4
  import React
5
5
 
6
6
  /// ComposerPianoImpl - Main implementation class for the Piano SDK on iOS.
7
- ///
8
- /// This class is responsible for:
9
- /// - Managing the Piano SDK's Composer instance.
10
- /// - Configuring the experience request using a fluent builder pattern.
11
- /// - Executing Piano experiences and handling results via a delegate.
12
- /// - Managing React Native promises across asynchronous callbacks.
13
7
  @objcMembers public class ComposerPianoImpl: NSObject {
14
8
 
15
9
  // MARK: - Properties
@@ -36,7 +30,7 @@ import React
36
30
  * @param aid The Piano Application ID.
37
31
  */
38
32
  public func initialize(aid: String, isSandbox: Bool) {
39
- print("ComposerPianoImpl: Initializing with AID: \(aid)")
33
+ print("[ComposerPianoImpl] Initializing with AID: \(aid), isSandbox: \(isSandbox)")
40
34
 
41
35
  // 1. Initialize and store the TokenService
42
36
  self.tokenService = TokenService()
@@ -45,8 +39,8 @@ import React
45
39
 
46
40
  // 2. Initialize the Composer instance
47
41
  let endpoint = isSandbox ? PianoEndpoint.sandbox : PianoEndpoint.production
42
+ print("[ComposerPianoImpl] Using endpoint: \(endpoint)")
48
43
  self.composer = PianoComposer(aid: aid, endpoint: endpoint)
49
- print("ComposerPianoImpl: Composer instance created for PRODUCTION")
50
44
 
51
45
  // 3. Initialize and set up the delegate
52
46
  self.delegate = MyComposerDelegate(impl: self)
@@ -83,34 +77,43 @@ import React
83
77
  // MARK: - Configuration Methods (Setters)
84
78
 
85
79
  public func addTag(_ tag: String) {
80
+ print("[ComposerPianoImpl] Adding tag: \(tag)")
86
81
  self.tags.append(tag)
87
82
  }
88
83
 
89
84
  public func addTags(_ tags: [String]) {
85
+ print("[ComposerPianoImpl] Adding tags: \(tags)")
90
86
  self.tags.append(contentsOf: tags)
91
87
  }
92
88
 
93
89
  public func setZoneId(_ zoneId: String) {
90
+ print("[ComposerPianoImpl] Setting zoneId: \(zoneId)")
94
91
  self.zoneId = zoneId
95
92
  }
96
93
 
97
94
  public func setReferrer(_ referrer: String) {
95
+ print("[ComposerPianoImpl] Setting referrer: \(referrer)")
98
96
  self.referrer = referrer
99
97
  }
100
98
 
101
99
  public func setUrl(_ url: String) {
100
+ print("[ComposerPianoImpl] Setting url: \(url)")
102
101
  self.url = url
103
102
  }
104
-
103
+
105
104
  public func setCustomVariables(_ variables: [String: String]) {
105
+ print("[ComposerPianoImpl] Setting custom variables")
106
106
  self.customVariables = variables
107
107
  }
108
108
 
109
109
  public func addCustomVariable(_ name: String, value: String) {
110
+ print("[ComposerPianoImpl] Adding custom variable: \(name) = \(value)")
110
111
  self.customVariables[name] = value
111
112
  }
112
113
 
113
114
  public func setUserToken(_ token: String) {
115
+ print("[ComposerPianoImpl] Setting userToken")
116
+ self.userToken = token
114
117
  _ = self.composer?.userToken(token)
115
118
  }
116
119
 
@@ -133,6 +136,7 @@ import React
133
136
  }
134
137
 
135
138
  public func clearConfiguration() {
139
+ print("[ComposerPianoImpl] Clearing all configurations")
136
140
  self.clearTags()
137
141
  self.clearCustomVariables()
138
142
  self.zoneId = nil
@@ -148,10 +152,13 @@ import React
148
152
  rejecter: @escaping RCTPromiseRejectBlock
149
153
  ) {
150
154
  guard let composer = self.composer else {
155
+ print("[ComposerPianoImpl] ERROR: executeExperience called but composer is not initialized.")
151
156
  rejecter("INIT_ERROR", "Composer not initialized.", nil)
152
157
  return
153
158
  }
154
159
 
160
+ print("[ComposerPianoImpl] Executing experience")
161
+
155
162
  self.experienceResolver = resolver
156
163
  self.experienceRejecter = rejecter
157
164
 
@@ -162,10 +169,18 @@ import React
162
169
  .referrer(self.referrer ?? "")
163
170
  .url(self.url ?? "")
164
171
 
165
- for (key, value) in self.customVariables {
166
- _ = composer.customVariable(name: key, value: value)
172
+ if !self.customVariables.isEmpty {
173
+ print("[ComposerPianoImpl] Applying \(self.customVariables.count) custom variables...")
174
+ for (key, value) in self.customVariables {
175
+ print("[ComposerPianoImpl] - Applying custom variable: '\(key)' = '\(value)'")
176
+ _ = composer.customVariable(name: key, value: value)
177
+ }
178
+ print("[ComposerPianoImpl] Custom variables applied successfully.")
179
+ } else {
180
+ print("[ComposerPianoImpl] No custom variables to apply.")
167
181
  }
168
182
 
183
+ print("[ComposerPianoImpl] Calling composer.execute()")
169
184
  composer.execute()
170
185
  }
171
186
  }
@@ -2,50 +2,26 @@ import Foundation
2
2
  import PianoComposer
3
3
  import React
4
4
 
5
- /**
6
- * MyComposerDelegate - Implements the PianoComposerDelegate protocol to handle
7
- * events from the native Piano SDK and communicate them back to React Native.
8
- */
5
+ /// MyComposerDelegate - Implements the PianoComposerDelegate protocol to handle
6
+ /// events from the native Piano SDK and communicate them back to React Native.
9
7
  @objcMembers public class MyComposerDelegate: NSObject, PianoComposerDelegate {
10
8
 
11
9
  // MARK: - Properties
12
10
 
13
- /**
14
- * A weak reference back to the main implementation class.
15
- * This is used to access the stored promise resolver and rejecter
16
- * and to avoid a strong reference cycle (memory leak).
17
- */
18
11
  private weak var impl: ComposerPianoImpl?
19
12
 
20
- // MARK: - Initialization
21
-
22
- /**
23
- * Initializes the delegate with a reference back to the main implementation.
24
- *
25
- * @param impl The instance of ComposerPianoImpl.
26
- */
27
13
  init(impl: ComposerPianoImpl) {
28
14
  self.impl = impl
29
15
  }
30
16
 
31
17
  // MARK: - Promise Handling Helpers
32
18
 
33
- /**
34
- * Resolves the stored promise with the provided event data.
35
- *
36
- * @param eventData A dictionary representing the event to be sent to JavaScript.
37
- */
38
19
  private func resolvePromise(with eventData: [String: Any]) {
39
20
  guard let resolver = self.impl?.experienceResolver else { return }
40
21
  resolver(eventData)
41
22
  clearPromiseHandlers()
42
23
  }
43
24
 
44
- /**
45
- * Rejects the stored promise with a given error.
46
- *
47
- * @param error The error that occurred.
48
- */
49
25
  private func rejectPromise(with error: Error) {
50
26
  guard let rejecter = self.impl?.experienceRejecter else { return }
51
27
  let errorCode = "EXECUTION_FAILED"
@@ -54,9 +30,6 @@ import React
54
30
  clearPromiseHandlers()
55
31
  }
56
32
 
57
- /**
58
- * Clears the stored promise handlers to prevent them from being called more than once.
59
- */
60
33
  private func clearPromiseHandlers() {
61
34
  self.impl?.experienceResolver = nil
62
35
  self.impl?.experienceRejecter = nil
@@ -64,46 +37,33 @@ import React
64
37
 
65
38
  // MARK: - PianoComposerDelegate Implementation
66
39
 
67
- /**
68
- * Called when a 'showTemplate' event is received from the Piano backend.
69
- * This is the primary event for displaying a paywall or other experience.
70
- */
71
- public func showTemplate(composer: PianoComposer, event: XpEvent, params: ShowTemplateEventParams?) {
72
- print("MyComposerDelegate: Received showTemplate event")
73
- let eventData: [String: Any] = [
74
- "eventType": "showTemplate",
75
- "templateId": params?.templateId ?? "",
76
- "templateVariantId": params?.templateVariantId ?? "",
77
- "displayMode": params?.displayMode.rawValue ?? "inline",
78
- "containerSelector": params?.containerSelector ?? ""
79
- ]
40
+ public func showTemplate(
41
+ composer: PianoComposer, event: XpEvent, params: ShowTemplateEventParams?
42
+ ) {
43
+ var eventData = paramsToDictionary(params)
44
+ eventData["eventType"] = "showTemplate"
80
45
  resolvePromise(with: eventData)
81
46
  }
82
47
 
83
- /**
84
- * Called when a meter is active.
85
- */
86
- public func meterActive(composer: PianoComposer, event: XpEvent, params: PageViewMeterEventParams?) {
87
- print("MyComposerDelegate: Received meterActive event")
88
- let eventData: [String: Any] = [
89
- "eventType": "meterActive",
90
- "meterName": params?.meterName ?? "",
91
- "views": params?.views ?? 0,
92
- "viewsLeft": params?.viewsLeft ?? 0,
93
- "maxViews": params?.maxViews ?? 0
94
- ]
48
+ public func meterActive(
49
+ composer: PianoComposer, event: XpEvent, params: PageViewMeterEventParams?
50
+ ) {
51
+ print(
52
+ "[MyComposerDelegate] Received meterActive event for meter: \(params?.meterName ?? "N/A")"
53
+ )
54
+ var eventData = paramsToDictionary(params)
55
+ eventData["eventType"] = "meterActive"
95
56
  resolvePromise(with: eventData)
96
57
  }
97
58
 
98
- /**
99
- * Called when a meter has expired.
100
- */
101
- public func meterExpired(composer: PianoComposer, event: XpEvent, params: PageViewMeterEventParams?) {
102
- print("MyComposerDelegate: Received meterExpired event")
103
- let eventData: [String: Any] = [
104
- "eventType": "meterExpired",
105
- "meterName": params?.meterName ?? ""
106
- ]
59
+ public func meterExpired(
60
+ composer: PianoComposer, event: XpEvent, params: PageViewMeterEventParams?
61
+ ) {
62
+ print(
63
+ "[MyComposerDelegate] Received meterExpired event for meter: \(params?.meterName ?? "N/A")"
64
+ )
65
+ var eventData = paramsToDictionary(params)
66
+ eventData["eventType"] = "meterExpired"
107
67
  resolvePromise(with: eventData)
108
68
  }
109
69
 
@@ -122,20 +82,24 @@ import React
122
82
  print("MyComposerDelegate: Received userSegmentFalse event")
123
83
  resolvePromise(with: ["eventType": "userSegmentFalse"])
124
84
  }
125
-
85
+
126
86
  /**
127
87
  * A general-purpose callback for when an experience is executed.
128
88
  */
129
- public func experienceExecute(composer: PianoComposer, event: XpEvent, params: ExperienceExecuteEventParams?) {
130
- print("MyComposerDelegate: Received experienceExecute event")
131
- // This is often a good place to resolve the promise if no other specific event is expected.
132
- resolvePromise(with: ["eventType": "experienceExecute"])
89
+ public func experienceExecute(
90
+ composer: PianoComposer, event: XpEvent, params: ExperienceExecuteEventParams?
91
+ ) {
92
+ var eventData = paramsToDictionary(params)
93
+ eventData["eventType"] = "experienceExecute"
94
+ resolvePromise(with: eventData)
133
95
  }
134
96
 
135
97
  /**
136
98
  * Called when an error occurs during the experience execution.
137
99
  */
138
- public func experienceExecutionFailed(composer: PianoComposer, event: XpEvent, params: FailureEventParams?) {
100
+ public func experienceExecutionFailed(
101
+ composer: PianoComposer, event: XpEvent, params: FailureEventParams?
102
+ ) {
139
103
  print("MyComposerDelegate: Received experienceExecutionFailed event")
140
104
  let error = NSError(
141
105
  domain: "PianoSDK",
@@ -144,5 +108,24 @@ import React
144
108
  )
145
109
  rejectPromise(with: error)
146
110
  }
147
- }
148
111
 
112
+ // MARK: - Helper Methods
113
+
114
+ private func paramsToDictionary(_ params: Any?) -> [String: Any] {
115
+ var dict = [String: Any]()
116
+ if let params = params {
117
+ let mirror = Mirror(reflecting: params)
118
+ for child in mirror.children {
119
+ if let label = child.label {
120
+ // Handle cases where the value might be an enum
121
+ if let rawValue = (child.value as? any RawRepresentable)?.rawValue {
122
+ dict[label] = rawValue
123
+ } else {
124
+ dict[label] = child.value
125
+ }
126
+ }
127
+ }
128
+ }
129
+ return dict
130
+ }
131
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-sdk-pianoio",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Piano io sdk integration",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/commonjs/index.js",