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.
- package/android/src/main/java/com/sdkpianoio/ComposerPianoImpl.kt +50 -28
- package/android/src/main/java/com/sdkpianoio/SdkPianoioModule.kt +114 -60
- package/android/src/main/java/com/sdkpianoio/TokenService.kt +37 -34
- package/ios/ComposerPianoImpl.swift +26 -11
- package/ios/MyComposerDelegate.swift +53 -70
- package/package.json +1 -1
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
84
|
+
Log.d(TAG, "TokenService initialized")
|
84
85
|
|
85
|
-
|
86
|
+
Log.d(TAG, "Initialization completed successfully")
|
86
87
|
} catch (e: Exception) {
|
87
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
239
|
-
|
251
|
+
Log.d(
|
252
|
+
TAG,
|
253
|
+
"Listener received: UserSegmentListener event: ${event.eventData}"
|
254
|
+
)
|
240
255
|
},
|
241
256
|
MeterListener { event ->
|
242
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
280
|
+
Log.e(TAG, "getExperience failed with exception", exception)
|
259
281
|
promise.reject("EXECUTION_ERROR", exception.message, exception)
|
260
282
|
}
|
261
|
-
|
283
|
+
Log.d(TAG, "getExperience() called successfully")
|
262
284
|
} catch (e: Exception) {
|
263
|
-
|
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) :
|
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
|
-
|
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 =
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
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))
|
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))
|
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 =
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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 =
|
263
|
-
|
264
|
-
|
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
|
-
*
|
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)
|
39
|
-
|
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
|
-
|
43
|
+
Log.d(TAG, "Initialization completed successfully")
|
49
44
|
} catch (e: Exception) {
|
50
|
-
|
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
|
-
|
60
|
+
Log.w(TAG, "signIn called but not initialized.")
|
65
61
|
return
|
66
62
|
}
|
67
|
-
|
68
|
-
val request = localClient.signIn()
|
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
|
-
|
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
|
-
|
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
|
-
|
83
|
+
Log.d(TAG, "Starting sign out network request")
|
87
84
|
localClient.signOut(token) {
|
88
85
|
if (it.isSuccess) {
|
89
|
-
|
90
|
-
this.cachedToken = null
|
86
|
+
Log.d(TAG, "Sign out successful")
|
87
|
+
this.cachedToken = null
|
91
88
|
callback(true)
|
92
89
|
} else {
|
93
|
-
|
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
|
-
|
103
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
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
|
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
|
-
|
166
|
-
|
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
|
-
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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(
|
130
|
-
|
131
|
-
|
132
|
-
|
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(
|
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
|
+
}
|